vue-cli 配置多页面应用时 chunk-vendors 优化-分割逻辑

3,624 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路

需求

最近项目中需要将vue-cli生成的单页应用配置为多页应用。整个项目分为官网和控制台,控制台引入了大量的第三方插件。这些插件在官网的开发中都用不到,官网只用到了tailwindcss。因此希望通过配置多页面实现代码的分割。官网保持轻量,只加载 vue vue-router axios 等必要的库,而其他的库如 ant-design-vue 等不需要加载到 官网 的页面中。

问题

当我以为只需要按照 vue-cli 文档的配置 vue-cli pages 进行配置,一切都会按照我的设想进行合理的分割时,我发现这个想法太天真了,即使我的 官网代码 只引用了 vue 等几个核心的 vue 生态的库。但是当打包完毕之后, 预览 官网 页面时,我发现官网引用了 chunk-venders ,这个文件包含了 所有的 node_modules 中的第三方库。

这样不行,要知道。官网面向潜在的客户,用户是没有耐心等待的。webpack的默认策略已经不适用了。因此我们需要对打包配置进行手动优化。

思路

首先,我们来理一下所有的 第三方 依赖

  • vue 官网 控制台
  • vue-router 官网 控制台
  • axios 官网 控制台
  • vuex 控制台
  • ant-design-vue 仅控制台
  • lodash 仅控制台
  • echarts 仅控制台
  • ... 仅控制台

经过分析发现:vue vue-router axios 是真正公共的代码。因此,如果我可以把这三个依赖打包成一个单独的 vendors-base.js;控制台其余的依赖 打包入 vendors-console.js;剩余的依赖打包入 chunk-vendors.js 就好了。

// vue.config.js
...
  pages: {
    console: {
      entry: 'src/main.js',
      template: 'public/console.html',
      filename: 'console.html',
      chunks: ['vendors-base', 'chunk-vendors', 'chunk-common', 'vendors-console', 'console']
    },
    index: {
      entry: 'packages/platform/src/main.js',
      template: 'public/index.html',
      filename: 'index.html',
      chunks: ['vendors-base', 'chunk-vendors', 'chunk-common', 'index']
    },
  },
  configureWebpack: {
    optimization: {
      splitChunks: {
        cacheGroups: {
          'vendors-base': {
            name: 'vendors-base',
            test: /[\\/]node_modules[\\/](vue|vue-router|axios)/,
            chunks: 'all',
            priority: 10,
            enforce: true
          },
          'vendors-console': {
            name: 'vendors-console',
            test: /[\\/]node_modules[\\/](ant-design-vue|moment)/,
            chunks: 'all',
            priority: 8,
            enforce: true
          },
          'chunk-vendors': {
            name: 'chunk-vendors',
            test: /[\\/]node_modules[\\/]/,
            chunks: 'all',
            priority: 1,
            enforce: true
          },
        },
      },
    },
  },
...

打包完成之后 index.html 被注入了 vendors-base chunk-vendors index console.html 被注入了 vendors-base vendors-console chunk-vendors console

再次预览发现,index.html 仅加载了必要的模块以及 chunk-vendors。 达到了我们想要的效果

进一步优化

虽然上述代码实现了第三方库打包的分割,但是有一个痛点:如果我或者其他开发需要为控制台添加一个新的第三方库(如d3),那么它需要重新修改上述的规则,否则d3 将被打包入 chunk-vendors ,而被 官网 代码引入。

- test: /[\\/]node_modules[\\/](ant-design-vue|moment)/,
+ test: /[\\/]node_modules[\\/](ant-design-vue|moment|d3)/,

太繁琐了,因为官网部分一般不需要引入第三方库,因此,新加入的库一般都需要打包入 vendors-console。下面我们来优化一下

动态分割

我们可以根据 package.json 中的 dependencies 动态划分。

const packageJson = require('./package.json');
// console.log('packageJson', typeof packageJson);

function resolve(dir) {
  return path.join(__dirname, dir);
}

const baseModules = ['vue', 'vue-router', 'axios'];
// 不存在于 base 和 platform 的都打入 console
const consoleModules = Object.keys(packageJson.dependencies).filter((key) => {
  return !baseModules.includes(key);
});
const buildModuleReg = function(arr) {
  return new RegExp(`[\\\\/]node_modules[\\\\/](${arr.join('|')})[\\\\/]`);
};
...
configureWebpack: {
    optimization: {
      splitChunks: {
        cacheGroups: {
          'vendors-base': {
            name: 'vendors-base',
            test: buildModuleReg(baseModules),
            chunks: 'all',
            priority: 10,
            enforce: true
          },
          'vendors-console': {
            name: 'vendors-console',
            test: buildModuleReg(consoleModules),
            chunks: 'all',
            priority: 8,
            enforce: true
          },
          'chunk-vendors': {
            name: 'chunk-vendors',
            test: /[\\/]node_modules[\\/]/,
            chunks: 'all',
            priority: 1,
            enforce: true
          },
        },
      },
    },
}

经过这样的改造,

  • 当我为控制台新加入一个依赖时,我不需要再修改此配置了。
  • 当我需要添加一个通用的依赖时,我只需要在 baseModules 中配置一下即可