Vue.js 项目打包优化实践
首先上结果:
- 把常用的 Vue,router,vuex,axios 的 runtime 包拆分了出来,改为 cdn;
- 另外就是对于自己编写的业务代码进行分包,根据路由进行懒加载,可以较好的提高首屏加载速度。
- 添加了全局 loading 来提高体验。
![[Build] @vue/cli 项目优化实践 - 图1](/uploads/projects/alfxjx@notes/bd36ca16ef54cf087069d850e4ce57ec.png)
优化前,可以看出包含了不少的runtime包![[Build] @vue/cli 项目优化实践 - 图2](/uploads/projects/alfxjx@notes/c275e5f437f4f3013fc369434c73a039.png)
经过优化体积大幅减小
不出意外core-js也可以改成CDN
配合静态资源的CDN加速,目前首页的速度大概是700ms,除了图片外加载的速度大概是200ms左右
abandon.workabandon.work
CDN 优化
CDN 优化是在 webpack 中实现的,主要分为环境切换,webpack 打包技巧和 html-webpack-plugin配置三个部分。
这部分的代码可以点击这个链接
环境切换
通过process.env.NODE_ENV来切换环境,因为在 dev 环境的时候,最好还是使用本地包,方便 debug 等,在生产环境的时候才需要添加 CDN.
本项目的vue.config.js中就是这样安排的,这样就可以在不同环境对 webpack 进行配置,类似于vue-cli2的配置类型:
# cli 2webpack.dev.jswebpack.prod.js
本项目中;
if (process.env.NODE_ENV === 'production') {webpackConfig['chainWebpack'] = config => {config.plugin('html').tap(args => {args[0].cdn = cdn;return args;});};webpackConfig['configureWebpack'] = config => {config['externals'] = {vue: 'Vue',vuex: 'Vuex','vue-router': 'VueRouter',axios: 'axios',};config['plugins'].push(new BundleAnalyzerPlugin());};}module.exports = webpackConfig;
具体操作涉及vue/cli3+的配置,具体可以看这里; 对于configureWebpack, 有配置式和函数式,当使用函数式的时候,添加插件的方法可以看上面的片段,这是官方文档里面没有的。
打包技巧
那么该怎么设置 cdn 呢?
这里需要两个 webpack 的配置项:
- webpack.externals
- webpack.plugins.html-webpack-plugin 具体也可以看代码,主要的思路就是在区分环境的基础上,在生产环境将需要 cdn 的包放在webpack.externals里面:
接着就是操作 plugin,这里选择链式操作,将需要填入的 cdn 连接的内容传给index.html模板: ```javascript let cdn = { js: [webpackConfig['configureWebpack'] = config => {config['externals'] = {vue: 'Vue',vuex: 'Vuex','vue-router': 'VueRouter',axios: 'axios',};};
], };// 可以写成动态版本号'//cdn.bootcss.com/vue/2.6.10/vue.runtime.min.js','//cdn.bootcss.com/vue-router/3.1.3/vue-router.min.js','//cdn.bootcss.com/vuex/3.1.2/vuex.min.js','//cdn.bootcss.com/axios/0.19.0/axios.min.js',
webpackConfig[‘chainWebpack’] = config => { config.plugin(‘html’).tap(args => { args[0].cdn = cdn; return args; }); };
<a name="QIb6M"></a>### html-plugin 配置上述的配置完成之后,需要在public/inedx.html中修改一下:<br /><div id="app"></div><br /><% if(process.env.NODE_ENV==='production'){htmlWebpackPlugin.options.cdn.js.forEach(function(item){ if(item){ %><br /><script type="text/javascript" src="<%= item %>"></script><br /><% }})} %><br /><!-- built files will be auto injected --><br />这个的目的就是当是生产环境的时候,遍历生成 CDN 的元素。这是一个模板语法,可以看看[html-webpack-plugin](https://www.npmjs.com/package/html-webpack-plugin)<a name="slpNm"></a>### 路由懒加载优化这里主要是根据[官方文档](https://router.vuejs.org/zh/guide/advanced/lazy-loading.html)的实践: 结合 Vue 的异步组件和 Webpack 的代码分割功能,轻松实现路由组件的懒加载。<br />// src/router/index.js<br />// 路由懒加载 博客分块<br />const Blog = () => import(/* webpackChunkName: "group-blog" */ '../views/Blog/Blog.vue');<br />const ContentPage = () => import(/* webpackChunkName: "group-blog" */ '../views/Blog/ContentPage.vue');<a name="X7TGJ"></a>### loading 插件为了美化加载的过程,自定义一个Loading插件,主要参考了[这些文章](https://juejin.im/search?query=vue%20loading&type=all)<br />选择了一个[蛮炫酷的动效](https://codepen.io/Chokcoco/pen/XWJPzjw)<br />功能:- 单例模式- 可以在axios拦截器中使用。<a name="tERWF"></a>### 更新 使用 useBuiltIns external core-jscli 默认的 @vue/cli-plugin-babel/preset 使用的是 babel-preset-app,而babel-preset-app其实是 extends 的 [@babel/preset-env](https://new.babeljs.io/docs/en/next/babel-preset-env.html) 并且设置了 默认的 uesBuiltIns 为 "usuage"<br />所以我们需要做的就是设置为 “entry即可”<br /><br /><br />参考链接: <br />[How to use core-js as an external CDN in webpack?](https://link.zhihu.com/?target=https%3A//stackoverflow.com/questions/59204262/how-to-use-core-js-as-an-external-cdn-in-webpack)<br />[@vue/babel-preset-app](https://link.zhihu.com/?target=https%3A//github.com/vuejs/vue-cli/tree/dev/packages/%2540vue/babel-preset-app)<br />[@vue/cli-plugin-babel](https://link.zhihu.com/?target=https%3A//github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel%23readme)<a name="KfeN3"></a>### 参考文章1. [webpack 使用 HtmlWebpackPlugin 进行 cdn 配置](https://www.jianshu.com/p/9248db0349fb)2. [vue 打包优化](https://www.jianshu.com/p/70d2fe63c943)3. [在 vue-cli 3.0 中根据不同环境动态注入 CDN](https://www.jianshu.com/p/62dde86d2b17)4. [webpack-cdn-plugin](https://github.com/shirotech/webpack-cdn-plugin)5. [webpack4 配置的最佳实践](https://juejin.im/post/5b304f1f51882574c72f19b0#heading-7)<a name="bu50V"></a>## [CDN+Vue-Cli优化打包体积引起的对环境变量的迷思](https://juejin.cn/post/6877044310798254088)需求: 在打包发布的时候,根据配置打包出将常用的库使用CDN的版本。<br />在vue-cli中,可以使用htmlPlugin配置生成的html模板文件,使用了cli中插值的能力,在模板中嵌入表达式,将预设的cdn的脚本标签添加到模板中,同时使用webpack的external功能将本来项目中间的库文件抽离。<br />html文件的模板:```html<div id="app"></div><% if(process.env.NODE_ENV==='production'&& process.env.VUE_APP_CDN ==='CDN'){htmlWebpackPlugin.options.cdn.js.forEach(function(item){ if(item){ %><script type="text/javascript" src="<%= item %>"></script><% }})} %><!-- built files will be auto injected -->
vue.config.js:// 生产环境配置cdnconst useCDN = process.env.VUE_APP_CDN ==='CDN'; // 是否开启CDNconst CDNWEB = "cdn.bootcdn.net/ajax/libs";let cdn = {js: [`//${CDNWEB}/vue/2.6.10/vue.runtime.min.js`,`//${CDNWEB}/vue-router/3.0.3/vue-router.min.js`,`//${CDNWEB}/vuex/3.0.1/vuex.min.js`,`//${CDNWEB}/axios/0.20.0/axios.min.js`,`//${CDNWEB}/view-design/4.3.2/iview.min.js`,`//${CDNWEB}/echarts/4.8.0/echarts.min.js`,],};if (NODE_ENV === "production") {webpackConfig["chainWebpack"] = (config) => {config.plugin("html").tap((args) => {args[0].cdn = cdn;return args;});};webpackConfig["configureWebpack"] = (config) => {if (useCDN) {config["externals"] = {vue: "Vue",vuex: "Vuex","vue-router": "VueRouter",axios: "axios","view-design": "iview",iview: "ViewUI",'echarts':'echarts'};}}}
官方文档在客户端侧代码中使用环境变量里面说:
除了 VUEAPP* 变量之外,在你的应用代码中始终可用的还有两个特殊的变量:
- NODE_ENV - 会是 “development”、”production” 或 “test” 中的一个。具体的值取决于应用运行的模式。
- BASE_URL - 会和 vue.config.js 中的 publicPath 选项相符,即你的应用会部署到的基础路径。
看上面的说明,默认添加了NODEENV和BASE_URL,别的挂在process.env上面的变量在打包到客户端的时候,是不起作用的,因此添加CDN的flag需要以VUE_APP开头。
挂载上之后,使用vue inspect > out.js,可以看到,添加了一个常驻的变量:
plugins:[new DefinePlugin({'process.env': {NODE_ENV: '"development"',// 添加了新的变量VUE_APP_CDN: '"CDN"',BASE_URL: '"/"'}}),]
这样配合添加的.env文件,就可以实现配置打包的时候选择是否使用CDN模式。
使用.env.cdn这样的环境配置的时候,如果是打包也需要在这个文件中添加NODE_ENV=production
VUE_APP_CDN = CDNNODE_ENV = production
不添加的话,因为我使用的打包方式是: vue-cli-service build —mode cdn,这样注入环境变量,不添加NODE_ENV的话就会按照`vue-cli-service build --mode development打包生成一个app.js文件了。
这样一来,配合gzip等手段,打包尺寸大降低。
// 之前(统计stat size)All (5.18 MB)js/chunk-vendors.849a5738.js (5.1 MB)js/app.f17827af.js (75.19 KB)// parsed sizeAll (1.56 MB)js/chunk-vendors.849a5738.js (1.52 MB)js/app.f17827af.js (35.36 KB)// gzip sizeAll (476.98 KB)js/chunk-vendors.849a5738.js (469.66 KB)js/app.f17827af.js (7.32 KB)// cdn 之后All (119.18 KB)js/app.2e115181.js (75.19 KB)js/chunk-vendors.aedee448.js (43.99 KB)// parsed sizeAll (54.01 KB)js/app.2e115181.js (35.54 KB)js/chunk-vendors.aedee448.js (18.46 KB)// cdn gzip sizeAll (14.42 KB)js/app.2e115181.js (7.39 KB)js/chunk-vendors.aedee448.js (7.03 KB)
