前言 Vue-Cli中内置了Webpack,但是配置文件和Webpack也不尽相同。
我们可以通过命令查看对应的Webpack配置。
对于优化主要是两个方面
所以不管是分析问题还是解决问题有围绕这连个方面进行处理。
Vue-Cli自带
cache-loader 会默认为 Vue/Babel/TypeScript 编译开启。文件会缓存在 node_modules/.cache 中。
如果你遇到了编译方面的问题,记得先清缓存目录之后再试试看。
thread-loader 会在多核 CPU 的机器上为 Babel/TypeScript 转译开启。
查看Vue-Cli中的Webpack配置 介绍
Vue-Cli脚手架会有webpack的很多默认行为,因此我们得知道基于Vue-Cli的项目,当前的webpack都配置了啥,然后才能做针对性的分析与优化。
vue-cli-service 暴露了 inspect 命令用于审查解析好的 webpack 配置。那个全局的 vue 可执行程序同样提供了 inspect 命令,这个命令只是简单的把 vue-cli-service inspect 代理到了你的项目中。
使用方式:
1 2 3 vue inspect --mode production > webpack.config.production.js vue inspect --mode development > webpack.config.development.js
输入命令后,在根目录会生产一个webpack.config.production.js文件
如果vue command not found的错可以全局安装注册一下vue命令
分析 查看构建时间 说明:https://www.npmjs.com/package/speed-measure-webpack-plugin
安装
1 npm install --save-dev speed-measure-webpack-plugin
使用
1 2 3 4 5 6 7 8 9 const SpeedMeasurePlugin = require ('speed-measure-webpack-plugin' )module .exports = { chainWebpack : config => { config .plugin ('speed-measure-webpack-plugin' ) .use (SpeedMeasurePlugin ) .end (); } }
或者
1 2 3 4 5 6 7 8 const SpeedMeasurePlugin = require ('speed-measure-webpack-plugin' );module .exports = { configureWebpack : (config ) => { config.plugins .push ( new SpeedMeasurePlugin (), ); }, };
查看构建库大小 VUE CLI内置工具 1 vue-cli-service build --report
成功后就会在项目目录下找到/dist/report.html
结果如下图所示:
如果报错
node_modules.bin\vue-cli-service.ps1,因为在此系统上禁止运行脚本。
执行之后就能运行上面的命令了
1 2 3 set-ExecutionPolicy RemoteSigned -Scope CurrentUser Get-ExecutionPolicy
webpack-bundle-analyzer Vue CLi就不用这个工具了,但是也可以配置,配置后运行项目打开项目页面的同时也会打开分析页面。
安装
1 npm install --save-dev webpack-bundle-analyzer
配置
Webpack配置
1 2 3 4 5 6 const BundleAnalyzerPlugin = require ('webpack-bundle-analyzer' ).BundleAnalyzerPlugin ;module .exports = { plugins : [ new BundleAnalyzerPlugin () ] }
Vue Cli配置
1 2 3 4 5 6 7 8 const BundleAnalyzerPlugin = require ('webpack-bundle-analyzer' ).BundleAnalyzerPlugin ;module .exports = { configureWebpack : { plugins : [ new BundleAnalyzerPlugin () ] } }
优化 构建速度优化 happypack 安装
1 npm install --save-dev happypack
配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 const HappyPack = require ('happypack' );const os = require ('os' );const happyThreadPool = HappyPack .ThreadPool ({ size : os.cpus ().length });module .exports = { configureWebpack : config => { return { module : { rules : [ { test : /\.js$/ , loader : 'happypack/loader?id=happyBabel' , exclude : /node_modules/ }, ] }, externals : { vue : "Vue" , "vue-router" : "VueRouter" , vuex : "Vuex" , echarts : "echarts" , axios : "axios" , }, plugins : [ new HappyPack ({ id : 'happyBabel' , loaders : [{ loader : 'babel-loader?cacheDirectory=true' , }], threadPool : happyThreadPool, verbose : true , }) ], } }, }
实测没啥效果
vue-cli-plugin-dll 1 npm install --save-dev vue-cli-plugin-dll
接下来就是dll的相关配置,将我们项目中的依赖使用dll插件进行动态链接,这样依赖就不会进行编译,从而极大地提高编译速度,因为这些插件没有编译,在vue.config.js中进行配置,也很简单
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 const path = require ("path" );module .exports = { pluginOptions : { dll : { entry : ["vue" , "vue-router" , "view-design" ], cacheFilePath : path.resolve (__dirname, "./public" ), open : true , inject : true } }, }
多入口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 const path = require ('path' )module .exports = { pluginOptions : { dll : { entry : { vue : ["vue" , "vue-router" , "vuex" ], ui : ["view-design" ], }, output : { path : path.join (__dirname, 'public/dll' ), filename : '[name].dll.js' , library : '[name]_[hash]' }, open : true , inject : true , } } }
配置好之后然后运行,进行你上面配置插件动态链接库的编译
dll编译完成后会在上面配置的目录下生成dll文件夹,就可以开始跑项目了,因为这些插件都不需要编译,跑起来很流畅,修改后的热更新速度更是显著提升。我以前修改一行代码热更新编译在30秒以上,使用这个以后基本十秒以内搞定。
多线程优化 Vue-Cli Vue-Cli已经内置,开启
1 2 3 module .exports = { parallel : true , }
parallel
Type: boolean
Default: require('os').cpus().length > 1
是否为 Babel 或 TypeScript 使用 thread-loader。
该选项在系统的 CPU 有多于一个内核时自动启用,仅作用于生产构建。
Webpack 1 npm install --save-dev thread-loader
配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 const path = require ("path" );module .exports = { module : { rules : [ { test : /\.js$/ , include : path.resolve ('src' ), use : [ "thread-loader" , ], }, ], }, };
缓存构建 Webpack 中几种缓存方式:
cache-loader
hard-source-webpack-plugin
以上这些缓存方式都有首次启动时的开销,即它们会让 “冷启动” 时间会更长,但是二次启动能够节省很多时间.
而 Vue-Cli 已经内置了 cache-loader 进行以下两个的缓存了
babel-loader 的 cacheDirectory 标志
vue-loader 的 cacheDirectory 标志
所以
Vue Cli没有必要添加HardSourceWebpackPlugin
HardSourceWebpackPlugin
注意
HardSourceWebpackPlugin 和 speed-measure-webpack-plugin 不能一起使用
详细说明
https://www.npmjs.com/package/hard-source-webpack-plugin
在启动项目时会针对项目生成缓存,若是项目无package或其他变化,下次就不用花费时间重新构建,直接复用缓存。
安装
1 npm install --save-dev hard-source-webpack-plugin
配置vue.config.js
为模块提供中间缓存,缓存路径是:node_modules/.cache/hard-source
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 const HardSourceWebpackPlugin = require ('hard-source-webpack-plugin' )module .exports = { configureWebpack : config => { config.plugin .push ( new HardSourceWebpackPlugin ({ root : process.cwd (), directories : [], environmentHash : { root : process.cwd (), directories : [], files : ['package.json' , 'yarn.lock' ] } }) ) } }
或者
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 const HardSourceWebpackPlugin = require ('hard-source-webpack-plugin' )module .exports = { configureWebpack : { plugins :[ new HardSourceWebpackPlugin ({ root : process.cwd (), directories : [], environmentHash : { root : process.cwd (), directories : [], files : ['package.json' , 'yarn.lock' ] } }) ] } }
更多配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 new HardSourceWebpackPlugin ({ cacheDirectory : "../node_modules/.cache/hard-source/[confighash]" , recordsPath : "../node_modules/.cache/hard-source/[confighash]/records.json" , configHash : function (webpackConfig ) { return require ("node-object-hash" )({ sort : false }).hash ( webpackConfig ); }, environmentHash : { root : process.cwd (), directories : [], files : ["../package-lock.json" , "../yarn.lock" ] }, info : { mode : "none" , level : "debug" }, cachePrune : { maxAge : 2 * 24 * 60 * 60 * 1000 , sizeThreshold : 100 * 1024 * 1024 } })
缩小文件检索解析范围 为避免无用的检索与递归遍历,可以使用alias指定引用时候的模块,noParse,对不依赖本地代码的第三方依赖不进行解析。
Vue-Cli默认已进行了如下配置
1 noParse: /^(vue|vue-router|vuex|vuex-router-sync)$/
配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 const getAliasPath = dir => join (__dirname, dir)module .exports = { configureWebpack : config => { config.module .noParse = /^(vue|vue-router|vuex|vuex-router-sync|lodash|echarts|axios|view-design)$/ } chainWebpack : config => { config.resolve .alias .set ('@' , getAliasPath ('src' )) .set ('assets' , getAliasPath ('src/assets' )) .set ('utils' , getAliasPath ('src/utils' )) .set ('views' , getAliasPath ('src/views' )) .set ('components' , getAliasPath ('src/components' )) } lintOnSave : !process.env .NODE_ENV !== 'production' , }
或者
1 2 3 4 5 6 7 8 module .exports = { configureWebpack :{ module : { noParse : /^(vue|vue-router|vuex|vuex-router-sync|lodash|echarts|axios|view-design)$/ } } }
import优化 运用这个插件能在代码使用了import语法的情况下,大大提高代码的编译速度。
安装 babel-plugin-dynamic-import-node
1 npm install --save-dev babel-plugin-dynamic-import-node
vue-cli3
修改babel.config.js文件
1 2 3 4 5 6 7 8 module .exports = { presets : ["@vue/cli-plugin-babel/preset" ], env : { development : { plugins : ["dynamic-import-node" ] } } };
vue.cli2
.babelrc文件
1 2 3 4 5 6 7 8 9 "env" : { "test" : { "plugins" : [] }, "development" :{ "presets" : ["env" , "stage-2" ], "plugins" : ["dynamic-import-node" ] } }
打包体积优化 不生成SourceMap production环境不生成SourceMap
1 2 3 4 module .exports = { lintOnSave : false , productionSourceMap : process.env .NODE_ENV !== "production" , }
图片压缩 image-webpack-plugin
对图片像素要求没很极致的,这个压缩还是可以使用的,压缩率肉眼看起来感觉是没太大区别。 这里注意一下,我没有对svg进行压缩,原因是压缩的svg,再通过构建时被打包成base64时,生成的base64会有问题,无法访问。
1 2 3 4 5 6 7 8 9 10 11 12 module .exports = { chainWebpack : config => { config.module .rule ('images' ) .test (/\.(png|jpe?g|gif)(\?.*)?$/ ) .use ('image-webpack-loader' ) .loader ('image-webpack-loader' ) .options ({ bypassOnDebug : true }) .end () } }
gzip压缩 使用 gzip 压缩代码,效果显著。
安装
1 npm install compression-webpack-plugin
配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 const CompressionWebpackPlugin = require ('compression-webpack-plugin' )module .exports = { configureWebpack : config => { if (process.env .NODE_ENV === 'production' ) { config.plugins .push ( new CompressionWebpackPlugin ({ test : /\.js$|\.html$|\.css$/ , threshold : 4096 }) ) } } }
配置CDN 老实说,我不用这个功能的,线上使用 cdn 总让我有一种不安全感,除非公司有自己的 cdn 库,不过这确实也是一种优化方案,效果也还不错。它的配置也很简单,在 externals 中配置,例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 module .exports = { configureWebpack : config => { if (process.env .NODE_ENV === 'production' ) { config.externals = { vue : 'Vue' , 'vue-router' : 'VueRouter' , axios : 'axios' } } } }
然后在 public/index.html 模板文件中引入 cdn 地址:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <!DOCTYPE html > <html > <head > <meta charset ="utf-8" /> <meta http-equiv ="X-UA-Compatible" content ="IE=edge" /> <meta name ="viewport" content ="width=device-width,initial-scale=1.0" /> <link rel ="icon" href ="<%= BASE_URL %>favicon.ico" /> <title > </title > <script src ="https://cdn.bootcdn.net/ajax/libs/vue/2.5.10/vue.min.js" > </script > <script src ="https://cdn.bootcdn.net/ajax/libs/vue-router/3.0.1/vue-router.min.js" > </script > <script src ="https://cdn.bootcdn.net/ajax/libs/axios/0.18.0/axios.min.js" > </script > </head > <body > <div id ="app" > </div > </body > </html >
我这里使用的是 bootcdn 的地址,需要注意版本问题。
也可以借助 HtmlWebpackPlugin 插件来方便插入 cdn 的引入。
使用 cdn 引入的方式虽然能极大改善网页加载速度,但我还是不会用这个功能,项目还不需要非得这样的优化,也怕 cdn 不稳定。
当然
也可以不用CDN,直接把JS复制到项目下,用相对路径引用即可。
借助 HtmlWebpackPlugin 插件来方便插入 cdn 的引入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 const IS_PRODUCTION = process.env .NODE_ENV === "production" ;const path = require ("path" );const cdn_production = { js : ["/librarys/vue@2.6.11/vue.min.js" ] }; const cdn_development = { js : ["/librarys/vue@2.6.11/vue.js" ] }; module .exports = { configureWebpack : { externals : { vue : "Vue" , }, }, chainWebpack : config => { config.plugin ("html" ).tap (args => { args[0 ].cdn = IS_PRODUCTION ? cdn_production : cdn_development; return args; }); } };
index.html中添加
1 2 3 <% for (var i in htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.js) { %> <script src ="<%= htmlWebpackPlugin.options.cdn.js[i] %>" > </script > <% } %>
Vue Cli配置说明 https://cli.vuejs.org/zh/guide/webpack.html
简单的配置方式 调整 webpack 配置最简单的方式就是在 vue.config.js 中的 configureWebpack 选项提供一个对象:
1 2 3 4 5 6 7 8 module .exports = { configureWebpack : { plugins : [ new MyAwesomeWebpackPlugin () ] } }
该对象将会被 webpack-merge 合并入最终的 webpack 配置。
链式操作 (高级) Vue CLI 内部的 webpack 配置是通过 webpack-chain 维护的。这个库提供了一个 webpack 原始配置的上层抽象,使其可以定义具名的 loader 规则和具名插件,并有机会在后期进入这些规则并对它们的选项进行修改。
它允许我们更细粒度的控制其内部配置。接下来有一些常见的在 vue.config.js 中的 chainWebpack 修改的例子。
提示
当你打算链式访问特定的 loader 时,vue inspect 会非常有帮助。
修改 Loader 选项 1 2 3 4 5 6 7 8 9 10 11 12 13 module .exports = { chainWebpack : config => { config.module .rule ('vue' ) .use ('vue-loader' ) .tap ( options => { return options }) } }
提示
对于 CSS 相关 loader 来说,我们推荐使用 css.loaderOptions 而不是直接链式指定 loader。这是因为每种 CSS 文件类型都有多个规则,而 css.loaderOptions 可以确保你通过一个地方影响所有的规则。
externals详解 externals 配置提供了不从 bundle 中引用依赖 的方式。
简单理解就是不通过npm下载的类库,在html文件中以script引入,然后在页面中使用import导入的这种方式
1 2 3 4 5 6 7 8 9 10 11 12 module .exports = { configureWebpack : config => { config.externals = { vue : 'Vue' , 'vue-router' : 'VueRouter' , axios : 'axios' , html2canvas : "html2canvas" } } }
如上配置 我们暴漏了html2canvas变量,那么代码中的
1 import html2canvas from "html2canvas" ;
依旧能正常运行,如果不配置这个我们代码中就只能这样写了
1 const html2canvas = window .html2canvas ;
也就是说我们如上配置就可以
完全不变动之前代码中的引用。
不建议删除npm引用的包,在npm install的时候可能会安装其他版本的包。
升级Vue-Cli 其实上面的优化效果都不明显,升级Vue-Cli的效果是最好的。
查看Vue-Cli版本
升级电脑的Vue-Cli版本
1 2 npm uninstall vue-cli -g npm install -g @vue/cli
因为vue-cli 5已经使用上webpack5,之前vue.config.js文件的一些webpack的配置是有一些调整的。
升级项目中的Vue-Cli版本
package.json
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 { "name" : "myproject" , "version" : "1.0.0" , "private" : true , "scripts" : { "serve" : "vue-cli-service serve" , "build" : "vue-cli-service build" , "report" : "vue-cli-service build --report" } , "dependencies" : { "core-js" : "~3.8.3" , "vue" : "~2.6.11" , "vue-router" : "~3.5.1" , "vuex" : "~3.6.2" , "uuid" : "~3.3.3" } , "devDependencies" : { "@babel/core" : "~7.12.16" , "@babel/eslint-parser" : "~7.12.16" , "@vue/cli-plugin-babel" : "~5.0.0" , "@vue/cli-plugin-eslint" : "~5.0.0" , "@vue/cli-plugin-router" : "~5.0.0" , "@vue/cli-plugin-vuex" : "~5.0.0" , "@vue/cli-service" : "~5.0.0" , "eslint" : "~7.32.0" , "eslint-config-prettier" : "~8.3.0" , "eslint-plugin-prettier" : "~4.0.0" , "eslint-plugin-vue" : "~8.0.3" , "less" : "~4.0.0" , "less-loader" : "~8.0.0" , "vue-template-compiler" : "~2.6.11" , "prettier" : "~2.4.1" , } }
注意
devDependencies中的都替换就行。
vue-template-compiler要和vue的版本一致。
core-js要用3.x版本。
要添加uuid,新版本的vue-cli中默认是没有的。
vue.config.js
1 2 3 4 5 const { defineConfig } = require ("@vue/cli-service" );module .exports = defineConfig ({ transpileDependencies : true , });
babel.config.js
该文件无变动。
1 2 3 module .exports = { presets : ["@vue/cli-plugin-babel/preset" ], };
.eslintrc.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 module .exports = { root : true , env : { node : true , }, extends : [ "plugin:vue/essential" , "eslint:recommended" , "plugin:prettier/recommended" , ], parserOptions : { parser : "@babel/eslint-parser" , }, rules : { "no-console" : process.env .NODE_ENV === "production" ? "warn" : "off" , "no-debugger" : process.env .NODE_ENV === "production" ? "warn" : "off" , }, };
jsconfig.json
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 { "compilerOptions" : { "target" : "es5" , "module" : "esnext" , "baseUrl" : "./" , "moduleResolution" : "node" , "paths" : { "@/*" : [ "src/*" ] } , "lib" : [ "esnext" , "dom" , "dom.iterable" , "scripthost" ] } }