webpack打包多页面,怎么按需分chunk
「12-01更新」:今天同事提示,其实 html-webpack-plugin 已经支持这个事儿了,参考:javascript - How to use Webpack 4 SplitChunksPlugin with HtmlWebpackPlugin for Multiple Page Application? - Stack Overflow
前几天,同事在群里问了这么一个问题:一个 vue-cli 启动的项目,使用多页面打包方式,打包出两个应用a和b。a应用引入vant库ipunt组件,b引入vant库list组件。如果把vant打成一个单独的包,里面会既有input也有list,所以a应用加载了它并不需要的list组件,b应用加载了它不需要的input组件。怎么优化?
其实vue-cli自带的分离逻辑,公共chunk最多只会分离出两部分,即chunk-vendors
和chunk-common
。这可以通过在vue项目下执行vue inspect
来查看。
其中 chunk-vendors
表示,将 node_modules 下所有被引用的模块打成一个chunk。可以看出,这样的粒度是很粗的。对一般应用可能足够,对于文章开头提到的需求就不能满足了。所以我们需要对webpack做配置,覆盖vue的默认配置。
vue-cli使用 vue.config.js
可以配置webpack,将配置写在 configureWebpack
字段即可,参考 vue-cli官方文档 。
我们新创建一个vue-cli多页面应用来作为演示,你可以下载下来看看。github链接见:github.com/neilzhang61…
在我们的示例项目 vant-tree-shaking
中,有两个应用入口 index
和 subpage
,其中 index.html 页面用到了 vant 的 Button
组件和 Switch
组件,subpage.html 页面用到了 vant 的 Badge
和 Switch
。
我们希望的是,共用的 Switch
允许被打包进 chunk-vendors
,只有 index 页面用到的 Button
能被打包进 chunk-vant-index
中,只有 subpage 页面用到的 Badge
能被打包进 chunk-vant-subpage
中。
实现这一需求的的核心文件是 vue.config.js ,其内容如下:
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
pages: {
index: {
// page 的入口
entry: 'src/main.js',
// 模板来源
template: 'public/index.html',
// 在 dist/index.html 的输出
filename: 'index.html',
// 当使用 title 选项时,
// template 中的 title 标签需要是 <title><%= htmlWebpackPlugin.options.title %></title>
title: 'Index Page',
// 在这个页面中包含的块,默认情况下会包含
// 提取出来的通用 chunk 和 vendor chunk。
chunks: ['chunk-vendors', 'chunk-vant-index', 'index']
},
// 当使用只有入口的字符串格式时,
// 模板会被推导为 `public/subpage.html`
// 并且如果找不到的话,就回退到 `public/index.html`。
// 输出文件名会被推导为 `subpage.html`。
subpage: {
entry: 'src/main2.js',
chunks: ['chunk-vendors', 'chunk-vant-subpage', 'subpage']
}
},
productionSourceMap: false,
configureWebpack: {
plugins: [
new BundleAnalyzerPlugin()
],
optimization: {
splitChunks: {
cacheGroups: {
vendors: {
name: 'chunk-vendors',
test: /[\\/]node_modules[\\/]/,
priority: -10,
chunks: 'initial'
},
vantIndex: {
name: 'chunk-vant-index',
// test: /[\\/]node_modules[\\/](vant)[\\/]/,
test (module) {
// console.log(module)
const size = module._chunks.size
let chunkName = ''
if (size === 1) {
chunkName = [...module._chunks.values()][0].name
}
let path = module.resource
if (!path) return false
path = path.replace(/\\/g, '/')
const result = path && /node_modules\/vant\n*/.test(path) && size === 1 && chunkName === 'index'
return result
},
minSize: 1,
priority: -5,
chunks: 'all',
},
vantSubpage: {
name: 'chunk-vant-subpage',
// test: /[\\/]node_modules[\\/](vant)[\\/]/,
test (module) {
// console.log(module)
const size = module._chunks.size
let chunkName = ''
if (size === 1) {
chunkName = [...module._chunks.values()][0].name
}
let path = module.resource
if (!path) return false
path = path.replace(/\\/g, '/')
const result = path && /node_modules\/vant\n*/.test(path) && size === 1 && chunkName === 'subpage'
return result
},
minSize: 1,
priority: -5,
chunks: 'all',
},
}
},
}
},
}
这个文件中,起作用的就是 vantIndex
和 vantSubpage
这两个对象。解释一下
name
字段表示,生成文件时,文件名叫什么。test
字段可以是正则表达式或一个函数。当其为函数时,如果返回true,则会将匹配到的modele
分到此 chunk。minSize
字段表示一个chunk的最小字节限制。默认为 20000 字节。我们将此改为1,不然chunk太小的话,即使满足test条件,也不会分离出单独的chunk来了。priority
字段表示优先级,当有多个规则都能匹配到的话,优先使用哪个。允许为负数,数字越大的优先级越高。chunks
字段表示从哪些chunks里面抽取代码,有三个可选字符串值 initial、async、all
可以看出,为了达到我们的目的,主要是决定 vantIndex
和 vantSubpage
中的 test 函数什么时候返回 true。
以 vantSubpage
为例,满足以下条件时,test
函数应该返回 true:
- 当前
module
是 vant 包下的模块。即/node_modules\/vant\n*/.test(path)
为 true,其中的path
从module.resource
可以取到。 - 当前
module
只被一个页面依赖。即module._chunks.size === 1
,其中module._chunks
是一个Set
,它存放的是依赖此module
的页面的描述对象。当它的长度为 1 时,就表示只有一个页面依赖此module
。 - 当前
module
被依赖的页面的名字是subpage
,即chunkName === 'subpage'
。
vantIndex
同理。
这样我们执行 yarn build
或 npm run build
后,就可以以我们希望的方式分离chunk了。
其中的 chunk-vant-index.d01042a3.js
和 chunk-vant-subpage.53f1df41.js
就是我们分离出来的两个页面不能共享的chunks。
我们使用 BundleAnalyzerPlugin
插件也可以看出 chunk-vendors.b4fd1d08.js
中只有vant的公共代码和 Switch
组件的代码,chunk-vant-index.d01042a3.js
中只有 Button
组件的代码,chunk-vant-subpage.53f1df41.js
中只有 Badge
。我们的目的已经达到。
虽然在此文章举的例子的场景下,分割出chunk的意义有限,但是了解了webpack分chunks的方式后,有其他需求也能轻松实现了。
参考
转载自:https://juejin.cn/post/6900934461660069902