• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

Vue2.5 Web App 项目搭建 (TypeScript版)

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

参考了几位同行的Blogs和StackOverflow上的许多问答,在原来的ng1加TypeScript以及Webpack的经验基础上,搭建了该项目,核心文件如下,供需要的人参考。

package.json

 1 {
 2   "name": "app",
 3   "version": "1.0.0",
 4   "description": "App package.json from the documentation, supplemented with testing support",
 5   "author": "",
 6   "private": true,
 7   "scripts": {
 8     "dev": "webpack-dev-server -d --inline --hot --env.dev",
 9     "build": "rimraf dist && webpack --progress --hide-modules"
10   },
11   "dependencies": {
12     "axios": "^0.18.0",
13     "bootstrap": "^4.0.0",
14     "bootstrap-vue": "^2.0.0-rc.2",
15     "element-ui": "^2.2.2",
16     "font-awesome": "^4.7.0",
17     "jointjs": "^2.0.1",
18     "jquery": "^3.3.1",
19     "js-md5": "^0.7.3",
20     "layui-src": "^2.2.5",
21     "linq": "^3.0.9",
22     "lodash": "^4.17.5",
23     "pdfmake": "^0.1.36",
24     "popper.js": "^1.14.1",
25     "tinymce": "^4.7.12",
26     "uuid": "^3.2.1",
27     "vue": "^2.5.16",
28     "vue-class-component": "^6.2.0",
29     "vue-echarts-v3": "^1.0.19",
30     "vue-i18n": "^7.6.0",
31     "vue-i18n-extensions": "^0.1.0",
32     "vue-lazyload": "^1.2.3",
33     "vue-pdf": "^3.3.1",
34     "vue-property-decorator": "^6.0.0",
35     "vue-router": "^3.0.1",
36     "vue-socket.io": "^2.1.1-b",
37     "vue-tinymce": "github:lpreterite/vue-tinymce",
38     "vue-video-player": "^5.0.2",
39     "vuex": "^3.0.1",
40     "vuex-class": "^0.3.0"
41   },
42   "engines": {
43     "node": ">=6.0.0",
44     "npm": ">= 3.0.0"
45   },
46   "browserslist": [
47     "> 1%",
48     "last 2 versions",
49     "not ie <= 8"
50   ],
51   "devDependencies": {
52     "@kazupon/vue-i18n-loader": "^0.3.0",
53     "@types/lodash": "^4.14.106",
54     "ajv": "^6.3.0",
55     "autoprefixer": "^8.2.0",
56     "babel-core": "^6.26.3",
57     "babel-helper-vue-jsx-merge-props": "^2.0.3",
58     "babel-loader": "^7.1.4",
59     "babel-plugin-syntax-dynamic-import": "^6.18.0",
60     "babel-plugin-syntax-jsx": "^6.18.0",
61     "babel-plugin-transform-vue-jsx": "^3.7.0",
62     "babel-preset-env": "^1.6.1",
63     "bootstrap-loader": "^2.2.0",
64     "clean-webpack-plugin": "^0.1.19",
65     "compression-webpack-plugin": "^1.1.11",
66     "copy-webpack-plugin": "^4.5.1",
67     "css-loader": "^0.28.11",
68     "cssnano": "^3.10.0",
69     "extract-text-webpack-plugin": "^3.0.2",
70     "file-loader": "^1.1.11",
71     "html-loader": "^0.5.5",
72     "html-webpack-plugin": "^3.1.0",
73     "image-webpack-loader": "^4.2.0",
74     "json-loader": "^0.5.7",
75     "node-sass": "^4.7.2",
76     "optimize-css-assets-webpack-plugin": "^3.2.0",
77     "postcss-import": "^11.1.0",
78     "postcss-loader": "^2.1.3",
79     "postcss-url": "^7.3.2",
80     "resolve-url-loader": "^2.3.0",
81     "rimraf": "^2.6.2",
82     "sass-loader": "^6.0.7",
83     "sass-resources-loader": "^1.3.3",
84     "style-loader": "^0.20.3",
85     "ts-loader": "^3.1.1",
86     "tslint": "^5.9.1",
87     "tslint-config-standard": "^7.0.0",
88     "tslint-loader": "^3.6.0",
89     "typescript": "^2.8.3",
90     "uglifyjs-webpack-plugin": "^1.2.5",
91     "url-loader": "^1.0.1",
92     "vue-loader": "^14.2.1",
93     "vue-style-loader": "^4.1.0",
94     "vue-template-compiler": "^2.5.16",
95     "webpack": "^3.1.0",
96     "webpack-dev-server": "^2.9.4",
97     "webpack-parallel-uglify-plugin": "^1.1.0"
98   }
99 }

webpack.config.js

  1 const {resolve} = require('path');
  2 const webpack = require('webpack');
  3 const CopyWebpackPlugin = require('copy-webpack-plugin');
  4 const HtmlWebpackPlugin = require('html-webpack-plugin');
  5 const ExtractTextPlugin = require('extract-text-webpack-plugin');
  6 const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin');
  7 const ParallelUglifyPlugin=require('webpack-parallel-uglify-plugin') ;
  8 const CompressionWebpackPlugin = require('compression-webpack-plugin');
  9 const CleanWebpackPlugin = require('clean-webpack-plugin');
 10 const url = require('url');
 11 const publicPath = '/public/';
 12 
 13 function getVueStyleLoader(isDev) {
 14     if (!isDev) {
 15         return ExtractTextPlugin.extract({
 16             fallback: "vue-style-loader",
 17             use: ["css-loader", "postcss-loader", "sass-loader",
 18                 "sass-resources-loader?resources=./src/common/style/sass-resources.scss"]
 19         });
 20     }
 21     return "vue-style-loader!css-loader?sourceMap!postcss-loader?sourceMap!sass-loader?sourceMap!sass-resources-loader?resources=./src/common/style/sass-resources.scss";
 22 }
 23 
 24 function getCssLoader(isDev) {
 25     if (!isDev) {
 26         return ExtractTextPlugin.extract({
 27             fallback: "style-loader",
 28             use: ["css-loader", "postcss-loader"]
 29         });
 30     }
 31     return [
 32         {loader: 'style-loader'},
 33         {loader: 'css-loader', options: {sourceMap: true}},
 34         {loader: 'postcss-loader', options: {sourceMap: true}},
 35     ];
 36 }
 37 
 38 function getScssLoader(isDev) {
 39     if (!isDev) {
 40         return ExtractTextPlugin.extract({
 41             fallback: "style-loader",
 42             use: ["css-loader", "postcss-loader", "sass-loader",
 43                 "sass-resources-loader?resources=./src/common/style/sass-resources.scss"]
 44         });
 45     }
 46     return [
 47         {loader: 'style-loader'},
 48         {loader: 'css-loader', options: {sourceMap: true}},
 49         {loader: 'postcss-loader', options: {sourceMap: true}},
 50         {loader: 'sass-loader', options: {sourceMap: true}},
 51         {
 52             loader: 'sass-resources-loader',
 53             options: {resources: './src/common/style/sass-resources.scss'}
 54         }
 55     ];
 56 }
 57 
 58 function getPlugins(isDev, plugins) {
 59     if (!isDev) {
 60         plugins.push(
 61             new ExtractTextPlugin({
 62                 filename: 'assets/css/[name].[contenthash:8].css',
 63                 // Setting the following option to `false` will not extract CSS from codesplit chunks.
 64                 // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack.
 65                 // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`,
 66                 // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110
 67                 allChunks: true,
 68             }),
 69             // Compress extracted CSS. We are using this plugin so that possible
 70             // duplicated CSS from different components can be deduped.
 71             new OptimizeCSSPlugin({
 72               assetNameRegExp: /\.css$/g,
 73               cssProcessor: require('cssnano'),
 74               cssProcessorOptions: { discardComments: {removeAll: true}},
 75               canPrint: true,
 76             }),
 77             new ParallelUglifyPlugin({
 78                 uglifyJS: {
 79                     output: {
 80                         comments:  false  //去掉注释
 81                     },
 82                     compress: {
 83                         warnings:  false,
 84                         drop_debugger:  true,
 85                         drop_console:  true
 86                     },
 87                     sourceMap: false,
 88                 }
 89             }),
 90             // new CompressionWebpackPlugin({
 91             //     asset: '[path].gz[query]', //目标文件名
 92             //     algorithm: 'gzip', //使用gzip压缩
 93             //     test: new RegExp( //满足正则表达式的文件会被压缩
 94             //         '\\.(' + ['js', 'css'].join('|') + ')$'
 95             //     ),
 96             //     threshold: 10240, //资源文件大于10240B=10kB时会被压缩
 97             //     minRatio: 0.8 //最小压缩比达到0.8时才会被压缩
 98             // }),
 99             new CopyWebpackPlugin([
100                 {
101                     from: resolve(__dirname, 'static'),
102                     to: resolve(__dirname, `../web/static`),
103                     ignore: ['.*']  //忽视.*文件
104                 },
105                 {
106                     from: 'favicon.ico',
107                     to: resolve(__dirname, '../web/'),
108                     force: true
109                 }], {}),
110             new webpack.DefinePlugin({
111                 'process.env': {
112                     NODE_ENV: JSON.stringify('production')
113                 }
114             }),
115             new CleanWebpackPlugin([
116                 `../web/${publicPath}/chunks`,
117                 `../web/${publicPath}/assets`,
118                 `../web/static`], {
119                 root: __dirname,
120                 verbose: true,
121                 dry: false,
122                 allowExternal: true
123             }),
124         );
125     }
126     return plugins;
127 }
128 
129 module.exports = (options = {}) => ({
130     entry: {
131         vendor: [
132             './src/vendor.ts',
133             `bootstrap-loader/lib/bootstrap.loader?${!options.dev ? 'extractStyles' : ''}&configFilePath=${__dirname}/.bootstraprc!bootstrap-loader/no-op.js`,
134             'lodash',
135             'linq'
136         ],
137         main: './src/main.ts'
138     },
139     output: {
140         path: resolve(__dirname, '../web' + publicPath),
141         filename: '[name].js',
142         chunkFilename: 'chunks/[name].[chunkhash:8].js',
143         publicPath: options.dev ? '/' : publicPath
144     },
145     resolve: {
146         extensions: ['.ts', '.tsx', '.js', '.vue', '.json'],
147         alias: {
148             'vue$': 'vue/dist/vue.esm.js',
149             '@': resolve(__dirname, 'src'),
150         }
151     },
152     module: {
153         rules: [
154             {
155                 test: /\.js$/,
156                 loader: 'babel-loader',
157                 // exclude: file => (
158                 //     /node_modules/.test(file) &&
159                 //     !/\.vue\.js/.test(file)
160                 // ),
161                 include: [
162                     resolve('src'),
163                     resolve('node_modules/vue-echarts-v3/src'),
164                     resolve('node_modules/vue-pdf/src')
165                 ]
166             },
167             {
168                 test: /\.tsx?$/,
169                 exclude: /node_modules/,
170                 enforce: 'pre',
171                 loader: 'tslint-loader'
172             },
173             {
174                 test: /\.tsx?$/,
175                 exclude: /node_modules|vue\/src/,
176                 use: [
177                     "babel-loader",
178                     {
179                         loader: "ts-loader",
180                         options: {
181                             appendTsSuffixTo: [/\.vue$/],
182                             transpileOnly: true,
183                         }
184                     }
185                 ]
186             },
187             {
188                 test: /\.vue$/,
189                 use: [{
190                     loader: 'vue-loader',
191                     options: {
192                         loaders: {
193                             js: "babel-loader",
194                             ts: "ts-loader!tslint-loader",
195                             tsx: "babel-loader!ts-loader!tslint-loader",
196                             scss: getVueStyleLoader(options.dev),
197                             i18n: "@kazupon/vue-i18n-loader"
198                         }
199                     }
200                 }]
201             },
202             {
203                 test: /\.css$/,
204                 use: getCssLoader(options.dev),
205             },
206             {
207                 test: /\.scss$/,
208                 use: getScssLoader(options.dev),
209                 exclude: /node_modules/
210             },
211             {
212                 test: /favicon\.png$/,
213                 use: [{
214                     loader: 'file-loader',
215                     options: {
216                         name: '[name].[ext]?[hash]'
217                     }
218                 }]
219             },
220             {
221                 test: /\.((woff2?|svg)(\?v=[0-9]\.[0-9]\.[0-9]))|(woff2?|svg|jpe?g|png|gif|ico)$/,
222                 exclude: /favicon\.png$/,
223                 use: [
224                     // 小于10KB的图片会自动转成dataUrl
225                     {
226                         loader: 'url-loader',
227                         options: {
228                             limit: 10240,
229                             name: "assets/image/[name].[hash:8].[ext]"
230                         }
231                     },
232                     {
233                         loader: 'image-webpack-loader',
234                         options: {
235                             query: {
236                                 mozjpeg: {
237                                     progressive: true,
238                                 },
239                                 gifsicle: {
240                                     interlaced: true,
241                                 },
242                                 optipng: {
243                                     bypassOnDebug: true,
244                                     progressive: true,
245                                     pngquant: {quality: "65-80", speed: 4}
246                                 }
247                             }
248                         }
249                     }
250                 ]
251             },
252             {
253                 test: /\.((ttf|eot)(\?v=[0-9]\.[0-9]\.[0-9]))|(ttf|eot)$/,
254                 use: [
255                     {
256                         loader: 'url-loader',
257                         options: {
258                             limit: 10240,
259                             name: "assets/font/[name].[hash:8].[ext]"
260                         }
261                     }]
262             },
263             {
264                 test: /\.json$/,
265                 loader: 'json-loader',
266                 exclude: /node_modules/
267             }
268         ],
269         loaders: [
270             {
271                 test: require.resolve('tinymce/tinymce'),
272                 loaders: [
273                     'imports?this=>window',
274                     'exports?window.tinymce'
275                 ]
276             },
277             {
278                 test: /tinymce\/(themes|plugins)\//,
279                 loaders: [
280                     'imports?this=>window'
281                 ]
282             }]
283     },
284     plugins: getPlugins(options.dev, [
285         new CopyWebpackPlugin([
286             { from: './node_modules/layui-src/dist/lay', to: './chunks/lay' },
287             { from: './node_modules/layui-src/dist/css', to: './chunks/css' },
288             { from: './node_modules/tinymce/plugins', to: './chunks/plugins' },
289             { from: './node_modules/tinymce/themes', to: './chunks/themes' },
290             { from: './node_modules/tinymce/skins', to: './chunks/skins' },
291             // {from: 'viewer',
292             // to: (options.dev ? '/' : resolve(__dirname, './build/public/viewer/')),
293             // force: true}
294         ], {}),
295         // split vendor js into its own file
296         new webpack.optimize.CommonsChunkPlugin({
297             name: 'vendor',
298             minChunks(module) {
299                 // any required modules inside node_modules are extracted to vendor
300                 return (
301                     module.resource &&
302                     /\.js$/.test(module.resource) &&
303                     module.resource.indexOf(
304                         resolve(__dirname, '../node_modules')
305                     ) === 0
306                 )
307             }
308         }),
309         // extract webpack runtime and module manifest to its own file in order to
310         // prevent vendor hash from being updated whenever app bundle is updated
311         new webpack.optimize.CommonsChunkPlugin({
312             name: 'manifest',
313             minChunks: Infinity,
314         }),
315         // This instance extracts shared chunks from code splitted chunks and bundles them
316         // in a separate chunk, similar to the vendor chunk
317         // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
318         new webpack.optimize.CommonsChunkPlugin({
319             name: 'main',
320             async: 'common',
321             children: true,
322             minChunks: 2
323         }),
324         new HtmlWebpackPlugin({
325             template: 'src/index.html',
326             filename: options.dev ? 'index.html' : resolve(__dirname, '../web/index.html'),
327             inject: true, //注入的js文件将会被放在body标签中,当值为'head'时,将被放在head标签中
328             chunks: ["manifest", "vendor", "common", "main"],
329             hash: true,
330             minify: {  //压缩配置
331                 removeComments: true, //删除html中的注释代码
332                 collapseWhitespace: true,  //删除html中的空白符
333                 removeAttributeQuotes: true  //删除html元素中属性的引号
334             },
335             chunksSortMode: 'dependency' //按dependency的顺序引入
336         }),
337         // 该处设定的参数可在程序中访问,但必须以/开头和结尾,并且/也会是值的一部分
338         new webpack.DefinePlugin({
339             __API_PATH__: options.dev ? '/api/' : '/ /',  // 此处必须写成/ / ,必须以/开头和结尾,且中间必须有一个空格
340             __ASSETS_PATH__: options.dev ? '/' : publicPath
341         }),
342         new webpack.ProvidePlugin({
343             _: 'lodash',
344             Enumerable: 'linq'
345         })
346     ]),
347     node: {
348         // prevent webpack from injecting useless setImmediate polyfill because Vue
349         // source contains it (although only uses it if it's native).
350         setImmediate: false,
351         // prevent webpack from injecting mocks to Node native modules
352         // that does not make sense for the client
353         dgram: 'empty',
354         fs: 'empty',
355         net: 'empty',
356         tls: 'empty',
357         child_process: 'empty'
358     },
359  
                       
                    
                    

鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
如何用TypeScript开发微信小程序发布时间:2022-07-18
下一篇:
typeScript之(5)打包发布时间:2022-07-18
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap