Mpx资源构建优化中,splitChunks代码分割改写如何实现长尾词优化?

2026-04-18 00:111阅读0评论SEO资源
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计2963个文字,预计阅读时间需要12分钟。

Mpx资源构建优化中,splitChunks代码分割改写如何实现长尾词优化?

背景+MPX是一款功能强大的小程序跨平台框架,核心是对原生小程序功能的增强。具体的使用方法并非本文讨论的范围,感兴趣者可前往官网了解更详细的内容。+使用MPX开发小程序

背景

MPX是滴滴出品的一款增强型小程序跨端框架,其核心是对原生小程序功能的增强。具体的使用不是本文讨论的范畴,想了解更多可以去官网了解更多。

Mpx资源构建优化中,splitChunks代码分割改写如何实现长尾词优化?

回到正题,使用MPX开发小程序有一段时间了,该框架对不同包之间的共享资源有一套自己的构建输出策略,其官网有这样一段描述说明:

总结关键的两点:

  • 纯js资源:主包引用则输出主包,或者分包之间共享也输出到主包
  • 非js资源,包括wxml、样式、图片等:主包引用则输出主包,分包之间共享则输出到各自分包

很好奇MPX内部是怎么做到上面这种效果的,尤其是js资源,于是就拜读了@mpxjs/webpack-plugin@2.6.61揭开其实现的细节。

mpx怎么实现的

首先简单介绍下MPX是怎么整合小程序离散化的文件结构,它基于webpack打包构建的,用户在Webpack配置中只需要配置一个入口文件app.mpx,它会基于依赖分析动态添加entry的方式来整合小程序的离散化文件,,loader会解析json配置文件中的pages域usingComponents域中声明的路径,通过动态添加entry的方式将这些文件添加到Webpack的构建系统当中,并递归执行这个过程,直到整个项目中所有用到的.mpx文件都加入进来。

重点来了,MPX在输出前,其借助了webpack的SplitChunksPlugin的能力将复用的模块抽取到一个外部的bundle中,确保最终生成的包中不包含重复模块。

js资源模块的输出

@mpxjs/webpack-plugin插件是MPX基于webapck构建的核心,其会在webpack所有模块构建完成的finishMoudles钩子中来实现构建输出策略,主要是配置SplitChunks的cacheGroup,后续webpack代码优化阶段会根据SplitChunks的配置来输出代码。

apply(compiler) { ... // 拿到webpack默认配置对象splitChunks let splitChunksOptions = compiler.options.optimization.splitChunks // 删除splitChunks配置后,webpack内部就不会实例化SplitChunkPlugin delete compiler.options.optimization.splitChunks // SplitChunkPlugin的实例化由mpx来接管,这样可以拿到其实例可以后续对其options进行修正 let splitChunksPlugin = new SplitChunksPlugin(splitChunksOptions) splitChunksPlugin.apply(compiler) ... compilation.hooks.finishModules.tap('MpxWebpackPlugin', (modules) => { // 自动跟进分包配置修改splitChunksPlugin配置 if (splitChunksPlugin) { let needInit = false Object.keys(mpx.componentsMap).forEach((packageName) => { if (!splitChunksOptions.cacheGroups.hasOwnProperty(packageName)) { needInit = true splitChunksOptions.cacheGroups[packageName] = getPackageCacheGroup(packageName) } }) if (needInit) { splitChunksPlugin.options = SplitChunksPlugin.normalizeOptions(splitChunksOptions) } } })

可以看出在所有模块构建完成时,针对不同的packageName来生成其对应的cacheGroups,主要体现在getPackageCacheGroup方法的实现。然后拿到SplitChunksPlugin实例的句柄,对其options进行重写规格化。

function isChunkInPackage (chunkName, packageName) { return (new RegExp(`^${packageName}\\/`)).test(chunkName) } function getPackageCacheGroup (packageName) { if (packageName === 'main') { return { name: 'bundle', minChunks: 2, chunks: 'all' } } else { return { test: (module, chunks) => { return chunks.every((chunk) => { return isChunkInPackage(chunk.name, packageName) }) }, name: `${packageName}/bundle`, minChunks: 2, minSize: 1000, priority: 100, chunks: 'all' } } }

getPackageCacheGroup会为小程序的每个包生成一个代码分割组,也就是生成每个包对应的cacheGroups

例如一个小程序项目有主包和A、B两个分包,其生成的cacheGroups内容如下:

{ default: { automaticNamePrefix: '', reuseExistingChunk: true, minChunks: 2, priority: -20 }, vendors: { automaticNamePrefix: 'vendors', test: /[\\/]node_modules[\\/]/, priority: -10 }, main: { name: 'bundle', minChunks: 2, chunks: 'all' }, A: { test: [Function: test], name: 'A/bundle', minChunks: 2, minSize: 1000, priority: 100, chunks: 'all' }, B: { test: [Function: test], name: 'B/bundle', minChunks: 2, minSize: 1000, priority: 100, chunks: 'all' }

分包代码分割输出bundle的优先级是最高的(priority: 100),所以会优先处理分包中的打包;否则会执行main中的代码打包规则,它会处理所有包之间的共享模块的打包以及主包中被复用的模块。

下面来看分包和主包的打包规则。

1、针对分包中的模块:

{ test: (module, chunks) => { // 依赖当前模块的所有chunks是否都是当前分包下的chunk return chunks.every((chunk) => { return isChunkInPackage(chunk.name, packageName) }) }, name: `${packageName}/bundle`, minChunks: 2, minSize: 1000, priority: 100, chunks: 'all' }

分包中的模块被抽离到当前分包下的bundle文件中,需满足:

  • 该模块没有被其他包引用,包括主包和其他分包(test函数逻辑
  • 至少被该分包下的2个chunk引用(minChunks:2
  • 抽离后的bundle大小最少满足 约1kb(minSize: 1000

2、针对主包中的模块:

{ name: 'bundle', minChunks: 2, chunks: 'all' }

会抽取到主包的bundle文件的条件:

  • 该模块至少被2个chunk引用(minChunks:2),这个chunk不区分主分包中的chunk

3、针对分包间共享的模块:

分包间共享的模块,不满足SplitChunks为每个分包设置的分包独享规则,即该模块只在当前分包引用,没有在其他包中被引用过。

test: (module, chunks) => { return chunks.every((chunk) => { return isChunkInPackage(chunk.name, packageName) }) }

所以,最终会走到main的cacheGroup的打包规则中,也就是主包的打包规则中。

这样,MPX通过配置SplitChunksOptions.cacheGroups来实现将主包中的js模块和分包共享的js模块都输出到主包,分包单独引用的模块输出到当前分包下。

组件和静态资源

对于组件和静态资源,MPX在webpack构建的thisCompilation钩子函数中会在compilation上挂载一个有关打包的__mpx__对象,包含静态资源、组件资源、页面资源等属性,也包含静态的非js资源的输出处理等:

compiler.hooks.thisCompilation.tap('MpxWebpackPlugin', (compilation, { normalModuleFactory }) => { ... if (!compilation.__mpx__) { mpx = compilation.__mpx__ = { ... componentsMap: { main: {} }, // 静态资源(图片,字体,独立样式)等,依照所属包进行记录,冗余存储,同上 staticResourcesMap: { main: {} }, ... // 组件和静态资源的输出规则如下: // 1. 主包引用的资源输出至主包 // 2. 分包引用且主包引用过的资源输出至主包,不在当前分包重复输出 // 3. 分包引用且无其他包引用的资源输出至当前分包 // 4. 分包引用且其他分包也引用过的资源,重复输出至当前分包 getPackageInfo: ({ resource, outputPath, resourceType = 'components', warn }) => { let packageRoot = '' let packageName = 'main' const { resourcePath } = parseRequest(resource) const currentPackageRoot = mpx.currentPackageRoot const currentPackageName = currentPackageRoot || 'main' const resourceMap = mpx[`${resourceType}Map`] const isIndependent = mpx.independentSubpackagesMap[currentPackageRoot] // 主包中有引用一律使用主包中资源,不再额外输出 if (!resourceMap.main[resourcePath] || isIndependent) { packageRoot = currentPackageRoot packageName = currentPackageName ... } resourceMap[packageName] = resourceMap[packageName] || {} const currentResourceMap = resourceMap[packageName] let alreadyOutputed = false if (outputPath) { outputPath = toPosix(path.join(packageRoot, outputPath)) // 如果之前已经进行过输出,则不需要重复进行 if (currentResourceMap[resourcePath] === outputPath) { alreadyOutputed = true } else { currentResourceMap[resourcePath] = outputPath } } else { currentResourceMap[resourcePath] = true } return { packageName, packageRoot, outputPath, alreadyOutputed } }, ... } } }

这样webpack构建编译非js资源时会调用compilation.__mpx__.getPackageInfo方法返回非js的静态资源的输出路径,在该方法内部制定了如下资源的的输出规则:

  1. 主包引用的资源输出至主包
  2. 分包引用且主包引用过的资源输出至主包,不在当前分包重复输出
  3. 分包引用且无其他包引用的资源输出至当前分包
  4. 分包引用且其他分包也引用过的资源,重复输出至当前分包

这样,mpx在处理项目中的静态资源时,会调用该方法获得静态资源的输出路径。

下面以一个简单例子来说明,

例如对项目中的图片会调用@mpxjs/webpack-plugin提供的url-loader进行处理,与webpack的url-loader类似,对于图片大小小于指定limit的进行base64处理,否则使用file-loader来输出图片(此时需要调用getPackageInfo方法获取图片的输出路径),相关代码:

let outputPath if (options.publicPath) { // 优先loader配置的publicPath outputPath = url if (options.outputPathCDN) { if (typeof options.outputPathCDN === 'function') { outputPath = options.outputPathCDN(outputPath, this.resourcePath, context) } else { outputPath = toPosix(path.join(options.outputPathCDN, outputPath)) } } } else { // 否则,调用getPackageInfo获取输出路径 url = outputPath = mpx.getPackageInfo({ resource: this.resource, outputPath: url, resourceType: 'staticResources', warn: (err) => { this.emitWarning(err) } }).outputPath } ... this.emitFile(outputPath, content); ...

最终,图片资源会调用compilation.__mpx__.getPackageInfo方法来获取图片资源的输出路径进行产出。

同样对于css资源wxml资源以及json资源,mpx内部是通过创建子编译器来抽取的,这里就不做深入介绍。

splitChunks的用法

webpack的splitChunks插件是用来进行代码拆分的,通过上面的分析可以看出MPX内部是通过内置splitChunkscacheGroups配置项来主动实现对小程序js模块实现分割优化的。webpack常见的代码分割方式有三种:

  • 多入口分割:webpack的entry配置项配置的手动入口,也包括可以使用compilation.addEntry程序添加的入口
  • 动态导入:通过模块的内联函数来分离代码,如通过import('./a')
  • 防止重复:使用splitChunks来去重和分离chunk

前两种在我们日常的开发中比较常见,第三种是通过webpack的optimization.splitChunks配置项来配置的。

通常情况下,webpack配置项optimization.splitChunks会有默认配置来实现代码分割,上面我们说到MPX在为不同包生成cacheGroups时,细心的同学会发现我们最终生成的包多了两个配置组:

{ default: { automaticNamePrefix: '', reuseExistingChunk: true, minChunks: 2, priority: -20 }, vendors: { automaticNamePrefix: 'vendors', test: /[\\/]node_modules[\\/]/, priority: -10 }, ... }

这是webpack为optimization.splitChunks.cacheGroups配置的默认组,除此之外optimization.splitChunks还有一些其他默认配置项,如下代码所示:

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 } } }

上面默认配置的实现的效果是:满足下面4个条件的模块代码会抽离成新的chunk

  • 来自node_modules中的模块,或者至少被2个chunk复用的模块代码
  • 分离出的chunk必须大于等于3000byte,约30kb
  • 按需异步加载chunk时,并行请求的最大数不超过5个
  • 页面初始加载时,并行请求的最大数不超过3个

下面来介绍下这些配置项的作用:

  • chunks:表示webpack将对哪些chunk进行分割,可选值为asyncallinitial
    • async:对于异步加载的chunks进行分割
    • initial:对非异步加载的初始chunks进行分割
    • all:对所有chunks进行分割
  • minSize: 分割后的chunk要满足的最小大小,否则不会分割
  • minChunks: 表示一个模块至少应被minChunks个chunk所包含才能分割
  • maxAsyncRequests: 表示按需加载异步chunk时,并行请求的最大数目;这个数目包括当前请求的异步chunk以及其所依赖chunk的请求
  • maxInitialRequests: 表示加载入口chunk时,并行请求的最大数目
  • automaticNameDelimiter: 表示拆分出的chunk的名称连接符,默认为。如chunkvendors.js
  • name: 设置chunk的文件名,默认为true,表示splitChunks基于chunk和cacheGroups的key自动命名。
  • cacheGroups: 通过它可以配置多个组,实现精细化分割代码;
    • 该对象配置属性继承splitChunks中除cacheGroups外所有属性,可以在该对象重新配置这些属性值覆盖splitChunks中的值
    • 该对象还有一些特有属性如testpriorityreuseExistingChunk

针对cacheGroups配置补充一点:

cacheGroups配置的每个组可以根据test设置条件,符合test条件的模块,就分配到该组。模块可以被多个组引用,但最终会根据priority来决定打包到哪个组中。

本文共计2963个文字,预计阅读时间需要12分钟。

Mpx资源构建优化中,splitChunks代码分割改写如何实现长尾词优化?

背景+MPX是一款功能强大的小程序跨平台框架,核心是对原生小程序功能的增强。具体的使用方法并非本文讨论的范围,感兴趣者可前往官网了解更详细的内容。+使用MPX开发小程序

背景

MPX是滴滴出品的一款增强型小程序跨端框架,其核心是对原生小程序功能的增强。具体的使用不是本文讨论的范畴,想了解更多可以去官网了解更多。

Mpx资源构建优化中,splitChunks代码分割改写如何实现长尾词优化?

回到正题,使用MPX开发小程序有一段时间了,该框架对不同包之间的共享资源有一套自己的构建输出策略,其官网有这样一段描述说明:

总结关键的两点:

  • 纯js资源:主包引用则输出主包,或者分包之间共享也输出到主包
  • 非js资源,包括wxml、样式、图片等:主包引用则输出主包,分包之间共享则输出到各自分包

很好奇MPX内部是怎么做到上面这种效果的,尤其是js资源,于是就拜读了@mpxjs/webpack-plugin@2.6.61揭开其实现的细节。

mpx怎么实现的

首先简单介绍下MPX是怎么整合小程序离散化的文件结构,它基于webpack打包构建的,用户在Webpack配置中只需要配置一个入口文件app.mpx,它会基于依赖分析动态添加entry的方式来整合小程序的离散化文件,,loader会解析json配置文件中的pages域usingComponents域中声明的路径,通过动态添加entry的方式将这些文件添加到Webpack的构建系统当中,并递归执行这个过程,直到整个项目中所有用到的.mpx文件都加入进来。

重点来了,MPX在输出前,其借助了webpack的SplitChunksPlugin的能力将复用的模块抽取到一个外部的bundle中,确保最终生成的包中不包含重复模块。

js资源模块的输出

@mpxjs/webpack-plugin插件是MPX基于webapck构建的核心,其会在webpack所有模块构建完成的finishMoudles钩子中来实现构建输出策略,主要是配置SplitChunks的cacheGroup,后续webpack代码优化阶段会根据SplitChunks的配置来输出代码。

apply(compiler) { ... // 拿到webpack默认配置对象splitChunks let splitChunksOptions = compiler.options.optimization.splitChunks // 删除splitChunks配置后,webpack内部就不会实例化SplitChunkPlugin delete compiler.options.optimization.splitChunks // SplitChunkPlugin的实例化由mpx来接管,这样可以拿到其实例可以后续对其options进行修正 let splitChunksPlugin = new SplitChunksPlugin(splitChunksOptions) splitChunksPlugin.apply(compiler) ... compilation.hooks.finishModules.tap('MpxWebpackPlugin', (modules) => { // 自动跟进分包配置修改splitChunksPlugin配置 if (splitChunksPlugin) { let needInit = false Object.keys(mpx.componentsMap).forEach((packageName) => { if (!splitChunksOptions.cacheGroups.hasOwnProperty(packageName)) { needInit = true splitChunksOptions.cacheGroups[packageName] = getPackageCacheGroup(packageName) } }) if (needInit) { splitChunksPlugin.options = SplitChunksPlugin.normalizeOptions(splitChunksOptions) } } })

可以看出在所有模块构建完成时,针对不同的packageName来生成其对应的cacheGroups,主要体现在getPackageCacheGroup方法的实现。然后拿到SplitChunksPlugin实例的句柄,对其options进行重写规格化。

function isChunkInPackage (chunkName, packageName) { return (new RegExp(`^${packageName}\\/`)).test(chunkName) } function getPackageCacheGroup (packageName) { if (packageName === 'main') { return { name: 'bundle', minChunks: 2, chunks: 'all' } } else { return { test: (module, chunks) => { return chunks.every((chunk) => { return isChunkInPackage(chunk.name, packageName) }) }, name: `${packageName}/bundle`, minChunks: 2, minSize: 1000, priority: 100, chunks: 'all' } } }

getPackageCacheGroup会为小程序的每个包生成一个代码分割组,也就是生成每个包对应的cacheGroups

例如一个小程序项目有主包和A、B两个分包,其生成的cacheGroups内容如下:

{ default: { automaticNamePrefix: '', reuseExistingChunk: true, minChunks: 2, priority: -20 }, vendors: { automaticNamePrefix: 'vendors', test: /[\\/]node_modules[\\/]/, priority: -10 }, main: { name: 'bundle', minChunks: 2, chunks: 'all' }, A: { test: [Function: test], name: 'A/bundle', minChunks: 2, minSize: 1000, priority: 100, chunks: 'all' }, B: { test: [Function: test], name: 'B/bundle', minChunks: 2, minSize: 1000, priority: 100, chunks: 'all' }

分包代码分割输出bundle的优先级是最高的(priority: 100),所以会优先处理分包中的打包;否则会执行main中的代码打包规则,它会处理所有包之间的共享模块的打包以及主包中被复用的模块。

下面来看分包和主包的打包规则。

1、针对分包中的模块:

{ test: (module, chunks) => { // 依赖当前模块的所有chunks是否都是当前分包下的chunk return chunks.every((chunk) => { return isChunkInPackage(chunk.name, packageName) }) }, name: `${packageName}/bundle`, minChunks: 2, minSize: 1000, priority: 100, chunks: 'all' }

分包中的模块被抽离到当前分包下的bundle文件中,需满足:

  • 该模块没有被其他包引用,包括主包和其他分包(test函数逻辑
  • 至少被该分包下的2个chunk引用(minChunks:2
  • 抽离后的bundle大小最少满足 约1kb(minSize: 1000

2、针对主包中的模块:

{ name: 'bundle', minChunks: 2, chunks: 'all' }

会抽取到主包的bundle文件的条件:

  • 该模块至少被2个chunk引用(minChunks:2),这个chunk不区分主分包中的chunk

3、针对分包间共享的模块:

分包间共享的模块,不满足SplitChunks为每个分包设置的分包独享规则,即该模块只在当前分包引用,没有在其他包中被引用过。

test: (module, chunks) => { return chunks.every((chunk) => { return isChunkInPackage(chunk.name, packageName) }) }

所以,最终会走到main的cacheGroup的打包规则中,也就是主包的打包规则中。

这样,MPX通过配置SplitChunksOptions.cacheGroups来实现将主包中的js模块和分包共享的js模块都输出到主包,分包单独引用的模块输出到当前分包下。

组件和静态资源

对于组件和静态资源,MPX在webpack构建的thisCompilation钩子函数中会在compilation上挂载一个有关打包的__mpx__对象,包含静态资源、组件资源、页面资源等属性,也包含静态的非js资源的输出处理等:

compiler.hooks.thisCompilation.tap('MpxWebpackPlugin', (compilation, { normalModuleFactory }) => { ... if (!compilation.__mpx__) { mpx = compilation.__mpx__ = { ... componentsMap: { main: {} }, // 静态资源(图片,字体,独立样式)等,依照所属包进行记录,冗余存储,同上 staticResourcesMap: { main: {} }, ... // 组件和静态资源的输出规则如下: // 1. 主包引用的资源输出至主包 // 2. 分包引用且主包引用过的资源输出至主包,不在当前分包重复输出 // 3. 分包引用且无其他包引用的资源输出至当前分包 // 4. 分包引用且其他分包也引用过的资源,重复输出至当前分包 getPackageInfo: ({ resource, outputPath, resourceType = 'components', warn }) => { let packageRoot = '' let packageName = 'main' const { resourcePath } = parseRequest(resource) const currentPackageRoot = mpx.currentPackageRoot const currentPackageName = currentPackageRoot || 'main' const resourceMap = mpx[`${resourceType}Map`] const isIndependent = mpx.independentSubpackagesMap[currentPackageRoot] // 主包中有引用一律使用主包中资源,不再额外输出 if (!resourceMap.main[resourcePath] || isIndependent) { packageRoot = currentPackageRoot packageName = currentPackageName ... } resourceMap[packageName] = resourceMap[packageName] || {} const currentResourceMap = resourceMap[packageName] let alreadyOutputed = false if (outputPath) { outputPath = toPosix(path.join(packageRoot, outputPath)) // 如果之前已经进行过输出,则不需要重复进行 if (currentResourceMap[resourcePath] === outputPath) { alreadyOutputed = true } else { currentResourceMap[resourcePath] = outputPath } } else { currentResourceMap[resourcePath] = true } return { packageName, packageRoot, outputPath, alreadyOutputed } }, ... } } }

这样webpack构建编译非js资源时会调用compilation.__mpx__.getPackageInfo方法返回非js的静态资源的输出路径,在该方法内部制定了如下资源的的输出规则:

  1. 主包引用的资源输出至主包
  2. 分包引用且主包引用过的资源输出至主包,不在当前分包重复输出
  3. 分包引用且无其他包引用的资源输出至当前分包
  4. 分包引用且其他分包也引用过的资源,重复输出至当前分包

这样,mpx在处理项目中的静态资源时,会调用该方法获得静态资源的输出路径。

下面以一个简单例子来说明,

例如对项目中的图片会调用@mpxjs/webpack-plugin提供的url-loader进行处理,与webpack的url-loader类似,对于图片大小小于指定limit的进行base64处理,否则使用file-loader来输出图片(此时需要调用getPackageInfo方法获取图片的输出路径),相关代码:

let outputPath if (options.publicPath) { // 优先loader配置的publicPath outputPath = url if (options.outputPathCDN) { if (typeof options.outputPathCDN === 'function') { outputPath = options.outputPathCDN(outputPath, this.resourcePath, context) } else { outputPath = toPosix(path.join(options.outputPathCDN, outputPath)) } } } else { // 否则,调用getPackageInfo获取输出路径 url = outputPath = mpx.getPackageInfo({ resource: this.resource, outputPath: url, resourceType: 'staticResources', warn: (err) => { this.emitWarning(err) } }).outputPath } ... this.emitFile(outputPath, content); ...

最终,图片资源会调用compilation.__mpx__.getPackageInfo方法来获取图片资源的输出路径进行产出。

同样对于css资源wxml资源以及json资源,mpx内部是通过创建子编译器来抽取的,这里就不做深入介绍。

splitChunks的用法

webpack的splitChunks插件是用来进行代码拆分的,通过上面的分析可以看出MPX内部是通过内置splitChunkscacheGroups配置项来主动实现对小程序js模块实现分割优化的。webpack常见的代码分割方式有三种:

  • 多入口分割:webpack的entry配置项配置的手动入口,也包括可以使用compilation.addEntry程序添加的入口
  • 动态导入:通过模块的内联函数来分离代码,如通过import('./a')
  • 防止重复:使用splitChunks来去重和分离chunk

前两种在我们日常的开发中比较常见,第三种是通过webpack的optimization.splitChunks配置项来配置的。

通常情况下,webpack配置项optimization.splitChunks会有默认配置来实现代码分割,上面我们说到MPX在为不同包生成cacheGroups时,细心的同学会发现我们最终生成的包多了两个配置组:

{ default: { automaticNamePrefix: '', reuseExistingChunk: true, minChunks: 2, priority: -20 }, vendors: { automaticNamePrefix: 'vendors', test: /[\\/]node_modules[\\/]/, priority: -10 }, ... }

这是webpack为optimization.splitChunks.cacheGroups配置的默认组,除此之外optimization.splitChunks还有一些其他默认配置项,如下代码所示:

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 } } }

上面默认配置的实现的效果是:满足下面4个条件的模块代码会抽离成新的chunk

  • 来自node_modules中的模块,或者至少被2个chunk复用的模块代码
  • 分离出的chunk必须大于等于3000byte,约30kb
  • 按需异步加载chunk时,并行请求的最大数不超过5个
  • 页面初始加载时,并行请求的最大数不超过3个

下面来介绍下这些配置项的作用:

  • chunks:表示webpack将对哪些chunk进行分割,可选值为asyncallinitial
    • async:对于异步加载的chunks进行分割
    • initial:对非异步加载的初始chunks进行分割
    • all:对所有chunks进行分割
  • minSize: 分割后的chunk要满足的最小大小,否则不会分割
  • minChunks: 表示一个模块至少应被minChunks个chunk所包含才能分割
  • maxAsyncRequests: 表示按需加载异步chunk时,并行请求的最大数目;这个数目包括当前请求的异步chunk以及其所依赖chunk的请求
  • maxInitialRequests: 表示加载入口chunk时,并行请求的最大数目
  • automaticNameDelimiter: 表示拆分出的chunk的名称连接符,默认为。如chunkvendors.js
  • name: 设置chunk的文件名,默认为true,表示splitChunks基于chunk和cacheGroups的key自动命名。
  • cacheGroups: 通过它可以配置多个组,实现精细化分割代码;
    • 该对象配置属性继承splitChunks中除cacheGroups外所有属性,可以在该对象重新配置这些属性值覆盖splitChunks中的值
    • 该对象还有一些特有属性如testpriorityreuseExistingChunk

针对cacheGroups配置补充一点:

cacheGroups配置的每个组可以根据test设置条件,符合test条件的模块,就分配到该组。模块可以被多个组引用,但最终会根据priority来决定打包到哪个组中。