基于Vue-Cli4构建项目的优化配置

1,779 阅读5分钟

基于Vue-Cli4构建项目的项目优化配置

前言

​ 从Vue-Cli3开始,Vue-Cli为我们提供了一个一键式的快速构建工具,在大部分简单项目中,它确保了各种构建工具能够基于智能的默认配置即可平稳衔接,这样我们可以专注在撰写应用上,而不必花好几天去纠结配置的问题。但编写合适的配置文件确是可以让我们的项目锦上添花,拥有更好的性能,并且拥有更好的结构。本文就基于个人的项目经验,总结一下比较常用的优化配置。

优化配置

  1. webpack-bundle-analyzer

    ​ 提到前端优化必然绕不开的打包分析插件,好在Vue-Cli已经替我们内置了,只需要在package.json中配置一下scripts:

    "scripts": {
        "serve": "vue-cli-service serve",
        "build": "vue-cli-service build",
        "analyzer": "vue-cli-service build --report"
      },
    

    ​ 就可以在打包的时候在生成一个report.html分析报告。

  2. productionSourceMap

    ​ Source map就是一个信息文件,里面储存着位置信息。转换后的代码的每一个位置,所对应的转换前的位置。有了它,出错的时候,debug将直接显示原始代码,而不是转换后的代码。

    ​ 大多数情况下,在生产环境下我们并不需要Source map文件,但是它又占了很大的体积,所以我们生产环境取消生成Source map文件。

    module.exports = {
    	productionSourceMap: false,
    }
    

    想要了解Source map的介绍,可以查看阮一峰老师的文章JavaScript Source Map 详解

  3. 添加别名 alias

    ​ 通常项目较大的时候我们会引入别名来方便引入,添加别名方法如下:

    module.exports = {
      chainWebpack: config => {
        // 添加别名
        config.resolve.alias
          .set("vue$", "vue/dist/vue.esm.js")
          .set("@", resolve("src"))
          .set("@assets", resolve("src/assets"))
          .set("@scss", resolve("src/assets/scss"))
          .set("@components", resolve("src/components"))
          .set("@plugins", resolve("src/plugins"))
          .set("@views", resolve("src/views"))
          .set("@router", resolve("src/router"))
          .set("@store", resolve("src/store"))
          .set("@layouts", resolve("src/layouts"))
          .set("@static", resolve("src/static"));
      }
    };
    

    ​ 但这个时候有些编辑器或编辑器插件会识别不了导致跳转定义失效,可以通过在项目根目录添加一个 alias.config.js 文件来解决这个问题,文件内容如下:

    const resolve = dir => require('path').join(__dirname, dir)
    
    module.exports = {
      resolve: {
        alias: {
          '@': resolve('src'),
          'static': resolve('static'),
          'public': resolve('public'),
        }
      }
    }
    
  4. 开启 Gzip 压缩

    Gzip是若干种文件压缩程序的简称,通常指GNU计划的实现,此处的gzip代表GNU zip。也经常用来表示gzip这种文件格式。目前主要讲的gzip压缩优化,就是通过gzip这个压缩程序,对资源进行压缩,从而降低请求资源的文件大小。压缩这个过程,可以在服务器接到请求的时候进行压缩,也可以在构建的时候进行压缩,比如我们在使用webpack打包的时候可以使用compression-webpack-plugin插件,在构建项目的时候进行gzip打包,比较通用的配置如下:

    const CompressionPlugin = require("compression-webpack-plugin")
    module.exports = {
    configureWebpack:config=>{
            if(progress.env.NODE_ENV === 'production'){
                return{
                    plugins: [
                    
                        new CompressionPlugin({
                            test:/\.js$|\.html$|.\css/, //匹配文件名
                            threshold: 10240,//对超过10k的数据压缩
                            deleteOriginalAssets: false //不删除源文件
                        })
                    ]
                }
            }
        },
    }
    
  5. 删除moment语言包

    ​ 删除 moment 除 zh-cn 中文包外的其它语言包,无需在代码中手动引入 zh-cn 语言包。

    const webpack = require("webpack");
    
    module.exports = {
      chainWebpack: config => {
        config
          .plugin("ignore")
          .use(
            new webpack.ContextReplacementPlugin(/moment[/\\]locale$/, /zh-cn$/)
          );
        return config;
      }
    };
    
  6. 配置 externals 引入 cdn 资源

    ​ 如果要用CDN引入一些资源,为了防止将某些 import 的包(package)打包到 bundle 中,而是在运行时(runtime)再去从外部获取这些扩展依赖,可以配置 externals 引入CDN资源:

    module.exports = {
      configureWebpack: config => {
        config.externals = {
          vue: "Vue",
          "element-ui": "ELEMENT",
          "vue-router": "VueRouter",
          vuex: "Vuex",
          axios: "axios"
        };
      },
      chainWebpack: config => {
        const cdn = {
          // 访问https://unpkg.com/element-ui/lib/theme-chalk/index.css获取最新版本
          css: ["//unpkg.com/element-ui@2.10.1/lib/theme-chalk/index.css"],
          js: [
            "//unpkg.com/vue@2.6.10/dist/vue.min.js", // 访问https://unpkg.com/vue/dist/vue.min.js获取最新版本
            "//unpkg.com/vue-router@3.0.6/dist/vue-router.min.js",
            "//unpkg.com/vuex@3.1.1/dist/vuex.min.js",
            "//unpkg.com/axios@0.19.0/dist/axios.min.js",
            "//unpkg.com/element-ui@2.10.1/lib/index.js"
          ]
        };
    
        // 如果使用多页面打包,使用vue inspect --plugins查看html是否在结果数组中
        config.plugin("html").tap(args => {
          // html中添加cdn
          args[0].cdn = cdn;
          return args;
        });
      }
    };
    
  7. splitChunks分包

    ​ webpack 4 移除 CommonsChunkPlugin,取而代之的是两个新的配置项(optimization.splitChunks 和 optimization.runtimeChunk),webpack通用模式现在已经做了一些通用性优化,适用于多数使用者。如果有需要,最好在实践测试的情况下,尝试手动优化这些参数,可以参考文章:一步一步的了解webpack4的splitChunk插件

    常用参数:

    • minSize(默认是30000):形成一个新代码块最小的体积
    • minChunks(默认是1):在分割之前,这个代码块最小应该被引用的次数(译注:保证代码块复用性,默认配置的策略是不需要多次引用也可以被分割)
    • maxInitialRequests(默认是3):一个入口最大的并行请求数
    • maxAsyncRequests(默认是5):按需加载时候最大的并行请求数。
    • chunks (默认是async) :initial、async和all
    • test: 用于控制哪些模块被这个缓存组匹配到。原封不动传递出去的话,它默认会选择所有的模块。可以传递的值类型:RegExp、String和Function
    • name(打包的chunks的名字):字符串或者函数(函数可以根据条件自定义名字)
    • priority :缓存组打包的先后优先级。
    module.exports = {
      configureWebpack: config => {
        if (progress.env.NODE_ENV === 'production') {
          config.optimization = {
            splitChunks: {
              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
                },
                elementUI: {
                  name: "chunk-hui",
                  test: /[\\/]node_modules[\\/]hui[\\/]/,
                  chunks: "all",
                  priority: 3,
                  reuseExistingChunk: true,
                  enforce: true
                },
                echarts: {
                  name: "chunk-echarts",
                  test: /[\\/]node_modules[\\/](vue-)?echarts[\\/]/,
                  chunks: "all",
                  priority: 4,
                  reuseExistingChunk: true,
                  enforce: true
                }
              }
            }
          };
        }
      },
      chainWebpack: config => {
        if (progress.env.NODE_ENV === 'production') {
          config.optimization.delete("splitChunks");
        }
        return config;
      }
    };
    
  8. 关于dll 和 AutoDllPlugin

    ​ 如果大家看过一些 webpack 优化的文章,有很大的概率会出现 AutoDllPlugin或者 dll 动态链接库。dll 的概念其实就是做缓存:

    所谓动态链接,就是把一些经常会共享的代码制作成 DLL 档,当可执行文件调用到 DLL 档内的函数时,Windows 操作系统才会把 DLL 档加载存储器内,DLL 档本身的结构就是可执行档,当程序有需求时函数才进行链接。透过动态链接方式,存储器浪费的情形将可大幅降低。

    ​ 不过在使用的时候,dll 常常因为繁琐的配置让人望而却步,好在 AutoDllPlugin 很大程度上简化了 dll 的配置,不过由于 webpack 4 有着比 dll 更好的打包性能, Vue 和 React 官方都已经抛弃使用 autodll-webpack-plugin ,这里就不再展开,有兴趣的可以阅读 辛辛苦苦学会的 webpack dll 配置,可能已经过时了

  9. HardSourceWebpackPlugin

    ​ dll 加速不明显了,有没有更好的替代品?在 AutoDllPlugin 的 README 里,给我们推荐了 HardSourceWebpackPlugin,初始配置更简单,只需要一行代码:

    const HardSourceWebpackPlugin = require('hard-source-webpack-plugin');
    module.exports = {
      plugins: [
        new HardSourceWebpackPlugin() // <- 直接加入这行代码就行
      ]
    }
    

    ​ 不过,webpack5 会内置 HardSource ,项目需求不强烈的话建议等 webpack5 更新。

  10. 关于 parallel

​ 如同上面讲到的 Dll 和 AutoDllPlugin ,像下面这样的 parallel 也经常出现在 webpack 优化的文章中。

module.exports = {
  parallel: require("os").cpus().length > 1,
};

​ Vue-Cli官方文档中也介绍过在 vue.config.js 文件中可以配置 parallel ,作用如下:

是否为 Babel 或 TypeScript 使用 thread-loader。 该选项在系统的 CPU 有多于一个内核时自动启用,仅作用于生产构建。

​ 但通过阅读源码,我们可以发现这是多余且不保险的。详见[Vue CLI 3] 配置解析之 parallel