vue-cli 3.0+
简 介
vue-cli 3.0相对2.x进行了断裂式的升级,配置方式有了巨大的改变,同时性能也有了很大的提升。最新的4.x延续了3.x,没有断裂式升级,只是增加部分新特性和局部优化。
- vue-cli 3.0+ 内置了webpack4,针对vue项目做了调整和优化,带有合理的默认配置最佳实践。
- vue-cli 3.0+ 新增了插件系统,集成了前端生态中最好的工具,并可使用vue add ...命令轻松新增插件。
- vue-cli 3.0+ 新增了可视化管理界面,可以可视化查看和修改配置,用vue ui命令启动。
目录结构中明显的改变就是去掉了2.x中的build/, config/构建配置的文件夹,改为在vue.config.js文件中修改配置。由于内置了webpack,对webpack配置更细颗粒的修改由webpack-chain的方式实现。
01
打包性能痛点
随着项目的复杂和功能的增加,依赖的第三方包越来越多,打包的性能问题逐渐变得无法忽视。总体来说,主要体现在两个方面:
- 构建过程时间太长
- 打包结果体积太大
针对这两个问题,就需要定制vue-cli的webpack配置,寻找打包优化策略。
针对时间太长的问题,可采用预编译策略;针对体积太大的问题,可采用分包拆包的策略。
现阶段较优的方案是通过webpack的splitChunks优化配置和DllPlugin插件配合相关插件来实现的。
- splitChunks主要是通过拆包减小单个包的体积
- DllPlugin是对依赖的第三方包通过预编译的方式提前构建,同时也支持分包构建
针对不同的需求和场景,大体的建议是:
如果打包速度尚可,只想拆包减小单个包的体积,只使用splitChunks即可;如果个别依赖库打包速度太慢,可以配合使用DllPlugin对其进行预编译;如果依赖库较多,同时还想将预编译的多个库也分包,那就无需使用splitChunks,全部交给DllPlugin处理。
02
splitChunks拆包
vue-cli 3.0+默认拆分配置会将node_modules中的依赖和src中的代码分开打成两个包:js/chunk-vendors[hash].js和js/app[hash].js。如果想颗粒度更细的拆包,就需要修改splitChunks配置了。
// vue.config.js const cacheGroups = require('./build/splitChunks') // 配置拆出单独文件方便管理 module.exports = { /* 链式配置:语法参考文档 webpack-chain API: https://github.com/Yatoo2018/webpack-chain/tree/zh-cmn-Hans */ chainWebpack: config => { if (process.env.NODE_ENV === 'production') { // 拆包优化 config.optimization.splitChunks({ chunks: 'all', // async表示抽取异步模块,all表示对所有模块生效,initial表示对同步模块生效 ...cacheGroups }) } }}
// build/splitChunks.jsmodule.exports = { cacheGroups: { common: { name: "chunk-common", chunks: "initial", minChunks: 2, maxInitialRequests: 5, minSize: 0, priority: 1, reuseExistingChunk: true, enforce: true }, vendors: { name: "chunk-vendors", test: /[\\/]node_modules[\\/]/, chunks: "initial", priority: 2, reuseExistingChunk: true, enforce: true }, polyfill: { name: "chunk-corejs", test: /[\\/]node_modules[\\/]core-js[\\/]/, chunks: "all", priority: 3, reuseExistingChunk: true, enforce: true } }}
cacheGroups(缓存组)是 Webpack splitChunks 最核心的配置,splitChunks的配置项都是作用于cacheGroup上的。其他参数说明如下:
- chunks: 'async' 三选一:"initial" | "all" | "async" (默认)
- minSize: 30000 最小尺寸,30K
- maxSize: 0 文件的最大尺寸,0为不限制
- minChunks: 1 默认1,被提取的一个模块至少需要在几个 chunk 中被引用,值越大抽取出来的文件就越小
- maxInitialRequests: 3 针对一个 entry 做初始化模块分隔的时候的最大文件数
- priority: 0 优先级,数字越大优先级越高
- reuseExistingChunk: true 是否使用已有的 chunk:如果该chunk包含的modules已经在另一个被分割的chunk中存在,那么直接引用已存在的chunk,不会再重新产生一个
- enforce: true 提升本组配置项的优先级,不会被splitChunks全局配置覆盖
需要拆分的包,在cacheGroups对象中依次添加即可。
修改配置后,要查看打包的效果,可以使用webpack-bundle-analyzer可视化工具插件,配置后,打包完成时会自动打开浏览器显示打包报告,可以可视化地查看打包情况。
// vue.config.jsconst BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPluginmodule.exports = { chainWebpack: config => { if (process.env.NODE_ENV === 'production') { // 配置webpack-bundle-analyzer打包分析插件 config.plugin('webpack-report').use(BundleAnalyzerPlugin, [ { analyzerMode: 'static' } ]) } }}
03
DllPlugin预编译
DllPlugin 是基于 Windows 动态链接库(dll)的思想被创作出来的,是webpack的内置插件。可以实现将第三方库提前编译打包到一个(不分包)或多个(分包)文件,打包一次后不需再随业务代码一起被重新打包(只有当依赖自身发生版本变化时才需要重新打包)。这种提前将第三方依赖库预编译抽离出来的办法,可以大幅提升打包速度。
DllPlugin需要配合另外两个插件一起使用,组成一个三件套,分三步走:
- 配置 dll 专属的配置文件,打包 dll 库,会打包出js和manifest.json描述文件
- 使用DllReferencePlugin插件,配置manifest.json描述文件,告诉webpack关联关系
- 使用add-asset-html-webpack-plugin插件将dll文件自动注入到打包后的index.html中
配置 dll 专属的配置文件,打包 dll 库
// build/webpack.dll.config.jsconst webpack = require('webpack')const path = require('path')const { CleanWebpackPlugin } = require('clean-webpack-plugin')const dllEntry = require('./dllEntry')module.exports = { mode: 'production', entry: { ...dllEntry // 多个入口拆出为单独文件,方便管理和复用 }, output: { path: path.join(__dirname, '../dll'), filename: '[name]_[hash:6].dll.js', library: '[name]_[hash]' }, plugins: [ new CleanWebpackPlugin(), // 每次打包时先清除已有文件 new webpack.DllPlugin({ context: path.resolve(__dirname, '../'), name: '[name]_[hash]', path: path.join(__dirname, '../dll', '[name].manifest.json') }) ]}
// build/dllEntry.js/* 要拆分预编译的包 */module.exports = { vue: [ 'vue/dist/vue.esm.js', // 可以指定vue为带 Compiler模式,默认为 runtime-only 模式 'vue-router', 'vuex', 'vue-i18n' ], polyfill: ['core-js'], element: ['element-ui'],}
dll专属配置文件配好后,在项目正式打包部署前需要先打包dll。可以配置一个npm script命令:
// package.json"scripts": { "dll": "webpack --progress --config ./build/webpack.dll.config.js", "build": "vue-cli-service build"}
记得安装相关依赖:
npm i -D webpack webpack-cli clean-webpack-plugin add-asset-html-webpack-plugin
运行npm run build之前,先运行npm run dll。这样就会生成抽离出来的对应库的js文件和manifest.json描述文件。
第一步完成。
使用DllReferencePlugin插件
第二步,在vue.config.js中配置DllReferencePlugin。
// vue.config.jsconst getDllManifest = require('./build/dllReference') // 关联插件拆分出单独文件便于管理module.exports = { configureWebpack: config => { let plugins = [] if (process.env.NODE_ENV === 'production') { // 为生产环境修改配置... // webpack.DllReferencePlugin plugins = [...getDllManifest()] config.plugins = [...config.plugins, ...plugins] } }}
// build/dllReference.jsconst path = require('path')const webpack = require('webpack')const dllEntry = require('./dllEntry') // 复用第一步中build/dllEntry.js文件暴露出的dllEntry/** 获取 dll文件的 manifest * @param {*} config */function getDllManifest () { const plugins = [] // 遍历dllEntry,有多个则添加多个manifest.json Object.keys(dllEntry).forEach((name) => { plugins.push( new webpack.DllReferencePlugin({ context: path.resolve(__dirname, '../'), // context必须与DllPlugin中的context一致 manifest: path.resolve(__dirname, '../dll/[name].manifest.json').replace(/\[name\]/gi, name) }) ) }) return plugins}module.exports = getDllManifest
第二步完成。
使用add-asset-html-webpack-plugin插件
最后一步,在vue.config.js中配置add-asset-html-webpack-plugin插件将dll文件自动注入到打包后的index.html中。
// vue.config.jsconst getDllManifest = require('./build/dllReference')module.exports = { configureWebpack: config => { let plugins = [] if (process.env.NODE_ENV === 'production') { // 为生产环境修改配置... // webpack.DllReferencePlugin plugins = [...getDllManifest()] // 插入dll到html plugins.push(new AddAssetHtmlPlugin({ // context: __dirname, filepath: path.resolve(__dirname, './dll/*.dll.js'), outputPath: '/dll', publicPath: '/dll' })) config.plugins = [...config.plugins, ...plugins] } }}
至此,DllPlugin全部配置完成。
验收成果
都配置好了,我们来验收下成果。
除了splitChunks拆包小节使用的webpack-bundle-analyzer可视化工具插件外,这里推荐使用vue-cli 3.0+提供的可视化管理工具。在终端运行vue ui命令,会启动一个监听8000端口的GUI服务,并自动打开浏览器,显示可视化操作界面。
导入我们的项目,点击左侧菜单任务,我们在package.json中配置的npm script命令会显示在任务列表中。
- 先选择dll,点击运行按钮,就可以看到我们打包的dll的情况。
- 再选择build,点击运行按钮,可以查看打包的情况、资源和速度统计。
- 最后我们检查下打包好的dist文件夹,看看js和dll文件夹,以及index.html中js和dll文件是否自动注入成功。
彩 蛋
由于vue-cli 3.0+内置了webpack,基于vue项目的最佳实践做了默认配置。对我们来说就成了一个黑盒子,不像vue-cli 2.x那样,可以直观的看到webpack的具体配置。
如果你不确定自己修改的webpack是否正确,是否生效,有两种方式可以检验。
- 可以使用vue-cli 3.0+可视化管理工具提供的inspect任务,点击运行,即可看到修改后webpack配置的最终结果。
- 可以使用终端命令vue inspect --mode production > webpack.ouput.js,运行后会生成一个js文件,在文件中可以自由搜索查找自己修改的配置。
这两种方式生成的文件,就是之前vue-cli 2.x中webpack的具体配置。