Webpack4之SplitChunksPlugin

前身 - CommonsChunkPlugin

webpack4以前,你们一直都是用CommonsChunkPlugin来作代码切割。那用他具体有什么坑呢?node

  1. 我用起来感受最诡异的点就是每次切割以后,新生成的chunk会和被提取的chunk生成一种父子关系(生成的公共chunk为entry chunk,以前有写过一篇文章介绍),即被提取的chunk会依赖于生成的chunk。下一次再作提取的时候,除非手动指定,只会扫描全部的entry chunk.react

  2. 感受CommonsChunkPlugin的表现也不是那么稳定,以前写了一个把node_modules里面的module全打包到vendor chunk里,明明minChunks属性设置成了Infinity,线上仍是出了锅,同事在node_modules里面装了一个包莫名其妙地被打到了业务代码里,害我出了一次事故。webpack

SplitChunksPlugin

升级了webpack4以后,production模式下,SplitChunksPlugin插件是默认被启用的,默认配置以下:web

splitChunks: {
    chunks: "async",
    minSize: 30000,
    minChunks: 1,
    maxAsyncRequests: 5,
    maxInitialRequests: 3,
    automaticNameDelimiter: '~',
    name: true,
    cacheGroups: {
        vendors: {
            test: /[\\/]node_modules[\\/]/,
            priority: -10
        },
    	default: {
            minChunks: 2,
            priority: -20,
            reuseExistingChunk: true
        }
    }
}
复制代码

这个配置是啥意思呢?老规矩,一个个参数的来看:正则表达式

  • chunks: 可填 async, initial, all. 顾名思义,async针对异步加载的chunk作切割,initial针对初始chunk,all针对全部chunk。
  • minSize: 咱们切割完要生成的新chunk要>30kb,不然不生成新chunk。
  • minChunks: 共享该module的最小chunk数
  • maxAsyncRequests:最多有5个异步加载请求该module
  • maxInitialRequests: 初始化的时候最多有3个请求该module
  • automaticNameDelimiter:名字中间的间隔符
  • name:chunk的名字,若是设成true,会根据被提取的chunk自动生成。
  • cacheGroups: 这个就是重点了,咱们要切割成的每个新chunk就是一个cache group。
    • test:和CommonsChunkPlugin里的minChunks很是像,用来决定提取哪些module,能够接受字符串,正则表达式,或者函数,函数的一个参数为module,第二个参数为引用这个module的chunk(数组).
    • priority:优先级高的chunk为被优先选择(说出来感受好蠢),优先级同样的话,size大的优先被选择
    • reuseExistingChunk: 当module未变时,是否可使用以前的chunk.

看到这里八成是一脸懵逼,这特么是在说啥?别急,具体来看几个例子就清楚了:数组

const entry = {
    index: "./src/index.jsx",
    index2: './src/index2.jsx',
}
复制代码

有index和index2两个entry(都引用了react和react-dom):缓存

import React from 'react';
import ReactDOM from 'react-dom';

import App from './App';

ReactDOM.render(<App />, document.getElementById('root'));
复制代码
import React from 'react';
import ReactDOM from 'react-dom';


console.log(React, ReactDOM);

复制代码

而后App.jsx长这样:bash

import React, { PureComponent } from 'react';
import { hot } from 'react-hot-loader'


class App extends PureComponent {
    render() {
        return (
            <div>App...</div>
        )
    }
}

export default hot(module)(App);
复制代码

根据默认配置,是不会有任何切割的,由于咱们只对async代码作切割。先把chunks改为initial,build获得以下结果:dom

如图,全部node_modules里面的引用都被打到了vendors~index~index2~.....里面. 如今咱们再加一个group:异步

vendors: {
    test: /[\\/]node_modules[\\/]/,
    priority: -10
},
'react-vendor': {
    test: (module, chunks) => /react/.test(module.context),
    priority: 1,
},
复制代码

build获得以下结果:

由于react-vendor的优先级高,因此react相关module都被打到了react-vendor~index~index2-.....里面。能够看到index和index2 chunk里都包含了fbjs 和lib包,这是由于这两个包过小了,放到一块儿生成的chunk size<咱们设置的30kb。

咱们把minSize设置成3kb,再次build:

能够看到fbjs和lib等小module被打到了vendors里面(react相关的module之因此被打到react-vendor 而不是vendor就是由于react-vendor的优先级高于vendor。 若是把react-vendor的优先级改为-11,则全部全部的module都会被打到vendor,不会生成react-vendor)。

接着咱们再来看一下minchunks,在index2里咱们引用moment

import React from 'react';
import ReactDOM from 'react-dom';
import moment from 'moment';


console.log(React, ReactDOM, moment);
复制代码

把vendor group的minchunks 设成2,覆盖掉splitChunks里面上面设置的1,build:

如图,moment由于只被index2引用了一次,因此不会被提取出来.

除了这种简单的提取,经过test咱们能够拿到module的信息(目前我只用过他的路径)和提取chunk的信息,而后本身进行组合来自定义生成新chunk。

vendors: {
    test: (module, chunks) => {
        let chunkName = '';
        chunks.forEach(chunk => {
            chunkName += chunk.name + ',';
        })
        console.log(module.context, chunkName, chunks.length);
        return /node_modules/.test(module.context)
    },
},
复制代码

另外,若是须要作持久缓存的话,虽然runtime能够经过设置runtimeChunk: true来解决,可是moduleID,ChunkID的问题仍是须要HashedModuleIdsPlugin等插件来解决(webpack实现持久缓存)。

参考文献

SplitChunkPlugin

webpack 4: Code Splitting, chunk graph and the splitChunks optimization

相关文章
相关标签/搜索