我是靠谱客的博主 迷路冬天,最近开发中收集的这篇文章主要介绍webpack如何更好地进行分包,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

1. 为什么需要分包?

为什么需要进行分包,一个大的 bundle.js 不好吗?

极其不建议,可从两方面进行考虑:

  1. 一行代码将导致整个 bundle.js 的缓存失效

  2. 一个页面仅仅需要 bundle.js 中 1/N 的代码,剩下代码属于其它页面,完全没有必要加载

2. 如何更好的分包?

2.1. 打包工具运行时

webpack(或其他构建工具) 运行时代码不容易变更,需要单独抽离出来,比如 webpack.runtime.js。由于其体积小,「必要时可注入 index.html 中」,减少 HTTP 请求数,优化关键请求路径

2.2. 前端框架运行时

React(Vue) 运行时代码不容易变更,且每个组件都会依赖它,可单独抽离出来 framework.runtime.js。请且注意,「务必将 React 及其所有依赖(react-dom/object-assign)共同抽离出来」,否则有可能造成性能损耗,见下示例

假设仅仅抽离 React 运行时(不包含其依赖)为单独 Chunk,且每个路由页面为单独 Chunk。某页面不依赖任何第三方库,则该页面会加载以下 Chunk

  1. webpack.runtime.js    5KB  ✅

  2. framework.runtime.js  30KB ✅

  3. page-a.chunk.js       50KB ✅

  4. vendor.chunk.js       50KB ❌ (因 webpack 依赖其 object-assign,而 object-assign 将被打入共同依赖 vendor.chunk.js,因此此时它必回加载,但是该页面并不依赖任何第三方库,完全没有必要全部加载 vendor.chunk.js)

将 React 运行时及其所有依赖,共同打包,修复结果如下,拥有了更完美的打包方案。

  1. webpack.runtime.js    5KB  ✅

  2. framework.runtime.js  40KB ✅  (+10KB)

  3. page-a.chunk.js       50KB ✅

2.3. 高频库

一个模块被 N(2个以上) 个 Chunk 引用,可称为公共模块,可把公共模块给抽离出来,形成 vendor.js

问:那如果一个模块被用了多次 (2次以上),但是该模块体积过大(1MB),每个页面都会加载它(但是无必要,因为不是每个页面都依赖它),导致性能变差,此时如何分包?

答:如果一个模块虽是公共模块,但是该模块体积过大,可直接 import() 引入,异步加载,单独分包,比如 echarts 等

问:如果公共模块数量多,导致 vendor.js 体积过大(1MB),每个页面都会加载它,导致性能变差,此时如何分包

答:有以下两个思路

  1. 思路一: 可对 vendor.js 改变策略,比如被引用了十次以上,被当做公共模块抽离成 verdor-A.js,五次的抽离为 vendor-B.js,两次的抽离为 vendor-C.js

  2. 思路二: 控制 vendor.js 的体积,当大于 100KB 时,再次进行分包,多分几个 vendor-XXX.js,但每个 vendor.js 都不超过 100KB

3. 使用 webpack 分包

在 webpack 中可以使用 SplitChunksPlugin 进行分包,它需要满足三个条件:

  1. minChunks: 一个模块是否最少被 minChunks 个 chunk 所引用

  2. maxInitialRequests/maxAsyncRequests: 最多只能有 maxInitialRequests/maxAsyncRequests 个 chunk 需要同时加载 (如一个 Chunk 依赖 VendorChunk 才可正常工作,此时同时加载chunk数为 2)

  3. minSize/maxSize: chunk 的体积必须介于 (minSize, maxSize) 之间

以下是 next.js 的默认配置,可视作最佳实践

{
// Keep main and _app chunks unsplitted in webpack 5
// as we don't need a separate vendor chunk from that
// and all other chunk depend on them so there is no
// duplication that need to be pulled out.
chunks: (chunk) =>
!/^(polyfills|main|pages/_app)$/.test(chunk.name) &&
!MIDDLEWARE_ROUTE.test(chunk.name),
cacheGroups: {
framework: {
chunks: (chunk: webpack.compilation.Chunk) =>
!chunk.name?.match(MIDDLEWARE_ROUTE),
name: 'framework',
test(module) {
const resource =
module.nameForCondition && module.nameForCondition()
if (!resource) {
return false
}
return topLevelFrameworkPaths.some((packagePath) =>
resource.startsWith(packagePath)
)
},
priority: 40,
// Don't let webpack eliminate this chunk (prevents this chunk from
// becoming a part of the commons chunk)
enforce: true,
},
lib: {
test(module: {
size: Function
nameForCondition: Function
}): boolean {
return (
module.size() > 160000 &&
/node_modules[/\]/.test(module.nameForCondition() || '')
)
},
name(module: {
type: string
libIdent?: Function
updateHash: (hash: crypto.Hash) => void
}): string {
const hash = crypto.createHash('sha1')
if (isModuleCSS(module)) {
module.updateHash(hash)
} else {
if (!module.libIdent) {
throw new Error(
`Encountered unknown module type: ${module.type}. Please open an issue.`
)
}
hash.update(module.libIdent({ context: dir }))
}
return hash.digest('hex').substring(0, 8)
},
priority: 30,
minChunks: 1,
reuseExistingChunk: true,
},
commons: {
name: 'commons',
minChunks: totalPages,
priority: 20,
},
middleware: {
chunks: (chunk: webpack.compilation.Chunk) =>
chunk.name?.match(MIDDLEWARE_ROUTE),
filename: 'server/middleware-chunks/[name].js',
minChunks: 2,
enforce: true,
},
},
maxInitialRequests: 25,
minSize: 20000,
}

最后

以上就是迷路冬天为你收集整理的webpack如何更好地进行分包的全部内容,希望文章能够帮你解决webpack如何更好地进行分包所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(69)

评论列表共有 0 条评论

立即
投稿
返回
顶部