本文章汇总了之前写的 2 篇文章。

Vue.js 项目打包优化实践

首先上结果:

  1. 把常用的 Vue,router,vuex,axios 的 runtime 包拆分了出来,改为 cdn;
  2. 另外就是对于自己编写的业务代码进行分包,根据路由进行懒加载,可以较好的提高首屏加载速度。
  3. 添加了全局 loading 来提高体验。

[Build] @vue/cli 项目优化实践 - 图1
优化前,可以看出包含了不少的runtime包
[Build] @vue/cli 项目优化实践 - 图2
经过优化体积大幅减小
不出意外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的配置类型:

  1. # cli 2
  2. webpack.dev.js
  3. webpack.prod.js

本项目中;

  1. if (process.env.NODE_ENV === 'production') {
  2. webpackConfig['chainWebpack'] = config => {
  3. config.plugin('html').tap(args => {
  4. args[0].cdn = cdn;
  5. return args;
  6. });
  7. };
  8. webpackConfig['configureWebpack'] = config => {
  9. config['externals'] = {
  10. vue: 'Vue',
  11. vuex: 'Vuex',
  12. 'vue-router': 'VueRouter',
  13. axios: 'axios',
  14. };
  15. config['plugins'].push(new BundleAnalyzerPlugin());
  16. };
  17. }
  18. module.exports = webpackConfig;

具体操作涉及vue/cli3+的配置,具体可以看这里; 对于configureWebpack, 有配置式和函数式,当使用函数式的时候,添加插件的方法可以看上面的片段,这是官方文档里面没有的。

打包技巧

那么该怎么设置 cdn 呢?
这里需要两个 webpack 的配置项:

  1. webpack.externals
  2. webpack.plugins.html-webpack-plugin 具体也可以看代码,主要的思路就是在区分环境的基础上,在生产环境将需要 cdn 的包放在webpack.externals里面:
    1. webpackConfig['configureWebpack'] = config => {
    2. config['externals'] = {
    3. vue: 'Vue',
    4. vuex: 'Vuex',
    5. 'vue-router': 'VueRouter',
    6. axios: 'axios',
    7. };
    8. };
    接着就是操作 plugin,这里选择链式操作,将需要填入的 cdn 连接的内容传给index.html模板: ```javascript let cdn = { js: [
    1. // 可以写成动态版本号
    2. '//cdn.bootcss.com/vue/2.6.10/vue.runtime.min.js',
    3. '//cdn.bootcss.com/vue-router/3.1.3/vue-router.min.js',
    4. '//cdn.bootcss.com/vuex/3.1.2/vuex.min.js',
    5. '//cdn.bootcss.com/axios/0.19.0/axios.min.js',
    ], };

webpackConfig[‘chainWebpack’] = config => { config.plugin(‘html’).tap(args => { args[0].cdn = cdn; return args; }); };

  1. <a name="QIb6M"></a>
  2. ### html-plugin 配置
  3. 上述的配置完成之后,需要在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)
  4. <a name="slpNm"></a>
  5. ### 路由懒加载优化
  6. 这里主要是根据[官方文档](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');
  7. <a name="X7TGJ"></a>
  8. ### loading 插件
  9. 为了美化加载的过程,自定义一个Loading插件,主要参考了[这些文章](https://juejin.im/search?query=vue%20loading&type=all)<br />选择了一个[蛮炫酷的动效](https://codepen.io/Chokcoco/pen/XWJPzjw)<br />功能:
  10. - 单例模式
  11. - 可以在axios拦截器中使用。
  12. <a name="tERWF"></a>
  13. ### 更新 使用 useBuiltIns external core-js
  14. cli 默认的 @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 />![](https://cdn.nlark.com/yuque/0/2021/png/502233/1620803879266-0ec566bc-0678-416e-97cb-785d93f1ac22.png#clientId=u8440a7a0-529b-4&from=paste&height=325&id=u38519931&margin=%5Bobject%20Object%5D&originHeight=325&originWidth=720&originalType=url&status=done&style=none&taskId=u9db9b616-9b25-40d0-a29e-fb50bddd059&width=720)<br />![](https://cdn.nlark.com/yuque/0/2021/png/502233/1620803878842-d357bf91-1193-43b0-9d51-63c92444fcbc.png#clientId=u8440a7a0-529b-4&from=paste&height=325&id=u5ef9a631&margin=%5Bobject%20Object%5D&originHeight=325&originWidth=720&originalType=url&status=done&style=none&taskId=ud6b91a25-4efd-4d90-89e2-09fa0ede5e8&width=720)<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)
  15. <a name="KfeN3"></a>
  16. ### 参考文章
  17. 1. [webpack 使用 HtmlWebpackPlugin 进行 cdn 配置](https://www.jianshu.com/p/9248db0349fb)
  18. 2. [vue 打包优化](https://www.jianshu.com/p/70d2fe63c943)
  19. 3. [在 vue-cli 3.0 中根据不同环境动态注入 CDN](https://www.jianshu.com/p/62dde86d2b17)
  20. 4. [webpack-cdn-plugin](https://github.com/shirotech/webpack-cdn-plugin)
  21. 5. [webpack4 配置的最佳实践](https://juejin.im/post/5b304f1f51882574c72f19b0#heading-7)
  22. <a name="bu50V"></a>
  23. ## [CDN+Vue-Cli优化打包体积引起的对环境变量的迷思](https://juejin.cn/post/6877044310798254088)
  24. 需求: 在打包发布的时候,根据配置打包出将常用的库使用CDN的版本。<br />在vue-cli中,可以使用htmlPlugin配置生成的html模板文件,使用了cli中插值的能力,在模板中嵌入表达式,将预设的cdn的脚本标签添加到模板中,同时使用webpack的external功能将本来项目中间的库文件抽离。<br />html文件的模板:
  25. ```html
  26. <div id="app"></div>
  27. <% if(process.env.NODE_ENV==='production'&& process.env.VUE_APP_CDN ==='CDN'){htmlWebpackPlugin.options.cdn.js.forEach(function(item){ if(item){ %>
  28. <script type="text/javascript" src="<%= item %>"></script>
  29. <% }})} %>
  30. <!-- built files will be auto injected -->
  1. vue.config.js:
  2. // 生产环境配置cdn
  3. const useCDN = process.env.VUE_APP_CDN ==='CDN'; // 是否开启CDN
  4. const CDNWEB = "cdn.bootcdn.net/ajax/libs";
  5. let cdn = {
  6. js: [
  7. `//${CDNWEB}/vue/2.6.10/vue.runtime.min.js`,
  8. `//${CDNWEB}/vue-router/3.0.3/vue-router.min.js`,
  9. `//${CDNWEB}/vuex/3.0.1/vuex.min.js`,
  10. `//${CDNWEB}/axios/0.20.0/axios.min.js`,
  11. `//${CDNWEB}/view-design/4.3.2/iview.min.js`,
  12. `//${CDNWEB}/echarts/4.8.0/echarts.min.js`,
  13. ],
  14. };
  15. if (NODE_ENV === "production") {
  16. webpackConfig["chainWebpack"] = (config) => {
  17. config.plugin("html").tap((args) => {
  18. args[0].cdn = cdn;
  19. return args;
  20. });
  21. };
  22. webpackConfig["configureWebpack"] = (config) => {
  23. if (useCDN) {
  24. config["externals"] = {
  25. vue: "Vue",
  26. vuex: "Vuex",
  27. "vue-router": "VueRouter",
  28. axios: "axios",
  29. "view-design": "iview",
  30. iview: "ViewUI",
  31. 'echarts':'echarts'
  32. };
  33. }
  34. }
  35. }

官方文档在客户端侧代码中使用环境变量里面说:
除了 VUEAPP* 变量之外,在你的应用代码中始终可用的还有两个特殊的变量:

  1. NODE_ENV - 会是 “development”、”production” 或 “test” 中的一个。具体的值取决于应用运行的模式
  2. BASE_URL - 会和 vue.config.js 中的 publicPath 选项相符,即你的应用会部署到的基础路径。

看上面的说明,默认添加了NODEENV和BASE_URL,别的挂在process.env上面的变量在打包到客户端的时候,是不起作用的,因此添加CDN的flag需要以VUE_APP开头。
挂载上之后,使用vue inspect > out.js,可以看到,添加了一个常驻的变量:

  1. plugins:[
  2. new DefinePlugin(
  3. {
  4. 'process.env': {
  5. NODE_ENV: '"development"',
  6. // 添加了新的变量
  7. VUE_APP_CDN: '"CDN"',
  8. BASE_URL: '"/"'
  9. }
  10. }
  11. ),
  12. ]

这样配合添加的.env文件,就可以实现配置打包的时候选择是否使用CDN模式。
使用.env.cdn这样的环境配置的时候,如果是打包也需要在这个文件中添加NODE_ENV=production

  1. VUE_APP_CDN = CDN
  2. NODE_ENV = production

不添加的话,因为我使用的打包方式是: vue-cli-service build —mode cdn,这样注入环境变量,不添加NODE_ENV的话就会按照`vue-cli-service build --mode development打包生成一个app.js文件了。
这样一来,配合gzip等手段,打包尺寸大降低。

  1. // 之前(统计stat size)
  2. All (5.18 MB)
  3. js/chunk-vendors.849a5738.js (5.1 MB)
  4. js/app.f17827af.js (75.19 KB)
  5. // parsed size
  6. All (1.56 MB)
  7. js/chunk-vendors.849a5738.js (1.52 MB)
  8. js/app.f17827af.js (35.36 KB)
  9. // gzip size
  10. All (476.98 KB)
  11. js/chunk-vendors.849a5738.js (469.66 KB)
  12. js/app.f17827af.js (7.32 KB)
  13. // cdn 之后
  14. All (119.18 KB)
  15. js/app.2e115181.js (75.19 KB)
  16. js/chunk-vendors.aedee448.js (43.99 KB)
  17. // parsed size
  18. All (54.01 KB)
  19. js/app.2e115181.js (35.54 KB)
  20. js/chunk-vendors.aedee448.js (18.46 KB)
  21. // cdn gzip size
  22. All (14.42 KB)
  23. js/app.2e115181.js (7.39 KB)
  24. js/chunk-vendors.aedee448.js (7.03 KB)

stat size parsed size 定义