Webpack4之SplitChunksPlugin规则

前言

从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名称上可以看出vendorsindex是index引用的第三方库,vendorsmain是main引用的第三方库,事实上也是如此。

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

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

纠正:

之前我认为是webpack根据代码结构自行分割(split),后来发现并非如此,这其实涉及到代码中动态加载(on demand loading)组件的概念

动态加载(on demanding loading)

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

那么一个新问题来了,webpack怎么判定index.js以及其verdors部分是动态的呢,难道有什么神奇的配置吗?

事实上并非如此,检查index.js的内容可以发现,其中的组件是在代码中写好要动态loading.

const IndexAsync = Loadable({
    loader: () => import(/* webpackChunkName: "index" */ './page/index/index'),
    loading() {
        return '';
    }
});

如果将动态load的组件改为同步加载,再打包就可以发现chunks中的index以及verdors-index都消失了,事实上这两部分被合入了main以及verdors~main。

import IndexSync from './page/index/index';

在这里插入图片描述

在这里插入图片描述

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时,vendorsmain没有了,事实上,vendorsmain的内容全部合并到了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,其他配置保持默认。发现本该正常生成的vendorsindex和vendorsmain都被合并到了vendor中。

name='vendor'

  • 7
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值