Webpack4之SplitChunksPlugin规则

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Napoleonxxx/article/details/81975186

前言

从Webpack3到Webpack4一个指标性的变化就是Webpack3的CommonsChunkPlugin被废弃了,取而代之的是Webpack4中的SplitChunksPlugin,这不仅仅是plugin名称的变化,也是分割chunk思想的变化。两种plugin的不同可以参照以下这篇文章:

webpack4:连奏中的进化

这篇文章的目的是记录一下对SplitChunksPlugin一些常用配置项的理解,通过某些配置项的用法来体会SplitChunksPlugin的分包思想。

工具: webpack-bundle-analyzer

该插件是将打包后的内容用canvas以图形的方式展示出来,借助这个工具,我们可以知道每个chunk由哪些模块组成,非常方便好用。配置如下:

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

module.exports = {
    ...
    plugins: [
        ...
        new BundleAnalyzerPlugin()
    ]
}

bundle example

SplitChunksPlugin 默认配置及规则

module.exports = {
  //...
  optimization: {
    splitChunks: {
      chunks: 'async',
      minSize: 30000,
      maxSize: 0,
      minChunks: 1,
      maxAsyncRequests: 5,
      maxInitialRequests: 3,
      automaticNameDelimiter: '~',
      name: true,
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true
        }
      }
    }
  }
};

默认规则(官网原文):

  • New chunk can be shared OR modules are from the node_modules folder
  • New chunk would be bigger than 30kb (before min+gz)
  • Maximum number of parallel requests when loading chunks on demand
    would be lower or equal to 5
  • Maximum number of parallel requests at initial page load would be lower or equal to 3

翻译过来大致意思如下(可能有不准确的地方)

  • 新代码块可以被共享引用,或这些模块都是来自node_modules
  • 新产出的vendor-chunk的大小得大于30kb
  • 按需加载的代码块(vendor-chunk)并行请求的数量小于或等于5个
  • 初始加载的代码块,并行请求的数量小于或者等于3个

SplitChunksPlugin 最简单配置结果分析

module.exports = {
    entry: [...],
    mode: 'production',  // development
    module: {
        rules: [...]
        ...
    }
    ...
    optimization: {
        ...
        splitChunks: {
            chunks: 'all'
        },
        runtimeChunk: {
            name: 'runtime'
        }
    }
}

非常简单,两个地方:splitChunks和runtimeChunk。我们先用上面的配置走一个看看chunk的情况。
bundle

chunks

可以看出共分了5个chunk。

业务:index.js, main.js (二者无交集)
第三方库:vendors~index.js, vendors~main.js (二者有交集)(可以参考引用文章webpack4:连奏中的进化为什么会出现这种现象)
runtime: runtime.js

从chunk名称上可以看出vendors~index是index引用的第三方库,vendors~main是main引用的第三方库,事实上也是如此。

业务代码为什么会被分为main和index两部分

我觉得这和项目的技术构成有关:React + canvas。项目大部分组件是用React和相关技术写的。但是有一块需要画图的业务完全是用canvas及相关图形库来做的。从依赖上看canvas与React是不相关的,完全没有必要去引用react以及相关的库。而canvas第三方库的大小又超过了30k所以webpack在做split时会将这两部分业务以及依赖的库分开,这样对第三方库也就是vendors~chunk做到了按需加载

模版index.html初始引用的script

template-index

打开生成的index.html,我们却只发现main和vendors~main,并没有发现index以及vendors~index。main出现在index.html很正常,因为我们项目的入口文件就打包在main里面。那么index.js去哪里了。

别着急,模版中引用了另外一个chunk即runtime.js,打开这个文件,我们会发现如下代码,可见业务2相关的代码由webpack4生成的runtime chunk来需要的时候动态引入(比如用户打开canvas)。
runtime-chunk

maxAsyncRequests

splitChunks配置规则中有一条:按需加载的代码块(vendor-chunk)并行请求的数量小于或等于5个

这句话具体是个什么意思?这句话其实就对应默认配置属性maxAsyncRequests

maxAsyncRequests(最大的异步请求数)和maxInitialRequests(最大的初始请求数是为了防止chunk划分的过于细致,导致大量的文件请求降低performance)。

还从上一次分包情况入手分析:vendors~index和vendors~main都是来自node_moudules。那么当前按需加载的代码块的并行请求数就是2。

我们设置maxAsyncRequests=2,发现分包结果和之前相同(既然一样就不上图了)

我们再设置maxAsyncRequests=1,配置以及分包结果如下:

        ...
        splitChunks: {
            chunks: 'all',
            maxAsyncRequests: 1
        },
        runtimeChunk: {
            name: 'runtime'
        }
        ....

maxAsyncRequests=1

可以看到,index和main还在,但是verdors只剩下了vendors~main chunk。可见按需加载(chunks on demand)的代码块指的就是vendors~chunk

maxInitialRequests

初始加载的代码块,并行请求的数量小于或者等于3个

这个是关于template index.html中放置的script数量。我们还是使用之前的分析方法:保持其他默认配置不变,我们分别将maxInitialRequests设置为2和1,两次index.html变化如下:

maxInitialRequests对比

maxInitialRequests=1

可以发现,当maxInitialRequests=1时,vendors~main没有了,事实上,vendors~main的内容全部合并到了main中。由此可以得出maxInitialRequests指的是模版html文件中,并行请求的javascript(不包括runtime)的数量。

chunks: all

请注意,上述配置中使用的chunks=’all’,其实这不是默认值,默认 entry 的 chunk 不会被拆分。chunks=’all’代表着我们拆分chunk时也包含entry。

做个实验验证一下:

        splitChunks: {
            // 默认值为3,我们再增加到10
            maxInitialRequests: 10
        }

chunks

我们发现,main作为entry并没有被拆分。

尾声:name

在使用splitChunks时,发现一个有意思的现象,权且记一下。

        splitChunks: {
            chunks: 'all',
            name: 'vendor'
        },
        runtimeChunk: {
            name: 'runtime'
        }

设置splitChunks中的name,其他配置保持默认。发现本该正常生成的vendors~index和vendors~main都被合并到了vendor中。

name='vendor'

猜你喜欢

转载自blog.csdn.net/Napoleonxxx/article/details/81975186