webpack splitChunks解析

module、chunk和bundle

要理解splitChunks,先要理解webpack的提出的几个概念,module、chunk和bundle。

  • module:每个import引入的文件就是一个模块(也就是直接手写的代码)
  • chunk:当module源文件传到webpack进行打包时,webpack会根据文件引用关系生成chunk(也就是module在webpack处理时是chunk)
  • bundle:是对chunk进行压缩等处理后的产出(也就是打包后可以直接运行的文件)
    在这里插入图片描述

webapck拆分出的chunk有四种:

  • 根据入口文件拆分
  • 根据异步文件拆分(import(‘…’))
  • 根据spliteChunk拆分
  • 还有一些运行时runtimeChunk,负责
    • 形如 import('abc').then(res=>{}) 这种异步加载的代码,在webpack中即为运行时代码
    • 每次更改所谓的运行时代码文件时,打包构建时app.js的hash值是不会改变的。如果每次项目更新都会更改app.js的hash值,那么用户端浏览器每次都需要重新加载变化的app.js,如果项目大切优化分包没做好的话会导致第一次加载很耗时,导致用户体验变差。现在设置了runtimeChunk,就解决了这样的问题。所以 这样做的目的是避免文件的频繁变更导致浏览器缓存失效,所以其是更好的利用缓存。提升用户体验。

splitChunks

默认配置:

module.exports = {
  //...
  optimization: {
    splitChunks: {
      chunks: 'async',
      minSize: 20000,
      minRemainingSize: 0,
      minChunks: 1,
      maxAsyncRequests: 30,
      maxInitialRequests: 30,
      enforceSizeThreshold: 50000,
      cacheGroups: {
        defaultVendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10,
          reuseExistingChunk: true,
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true,
        },
      },
    },
  },
};

假如我们有这样一个配置

module.exports = {
 entry: {
      app: './src/index'
 },
 output: {
   path: path.resolve(__dirname, 'dist'),
   filename: '[name].[hash:8].bundle.js', // 用于以entry为入口的chunk
   chunkFilename: '[name].[hash:16].bundle.js' // 用于动态加载的chunk
 }
}

// .src/index.js
const test = 123
console.log(test)
import _ from 'lodash'
setTimeout(()=> {
    import(/* webpackChunkName: 'test' */ './test')
    import(/* webpackChunkName: 'math' */ './math')
}, 2000)

// ./test.js
import './helper'

// ./math.js
import './helper'

// ./helper.js
// 从jQuery官网download的源码,helper.js Size > 30kb

默认在build的时候,webpack会将lodash和业务逻辑都打包到一个bundle中,这个bundle非常的大
在这里插入图片描述

splitChunks.chunks

async,表明只能分离异步的模块
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RhjjxFS6-1661777263263)(/tencent/api/attachments/s3/url?attachmentid=756452)]

  • app.js下的bundle包含了lodash模块,而math和test两个文件由于share了同一个helper,并且这个helper的size大于30kb,因此被分离出来,成为一个新的bundle

chunks:initial

当为initial时,只能分离初始化的模块,异步的模块无法分离
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BZ1VCcBn-1661777263263)(/tencent/api/attachments/s3/url?attachmentid=756453)]

  • app.js里的lodash被分离成一个bundle,而math和test里的js并没有分离出来,没有处理异步模块内的内容

chunks:all

将初始化或者异步的模块都分离出来
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2CxVQJpL-1661777263264)(/tencent/api/attachments/s3/url?attachmentid=756455)]

  • lodash和helper都被分离成单个bundle了

当splitChunks.chunk为一个函数时,可以自行选择哪些chunk需要被分离

optimization: {
 splitChunks: {
   chunks (chunk) {
      return chunk.name !== 'test' && chunk.name !== 'math'
   }
 }
}
  • lodash被分离成单个bundle,而helper并没有被分离出来
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UoL7SIw3-1661777263265)(/tencent/api/attachments/s3/url?attachmentid=756478)]

splitChunks.maxAsyncRequests

异步文件的最大并行请求数量

optimization: {
 splitChunks: {
   maxAsyncRequests: 1 // 2
 }
}

在build的时候会发现,这时候webpack并没有帮我们做codeSplitting
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LI5IvBPL-1661777263265)(/tencent/api/attachments/s3/url?attachmentid=756498)]

将maxAsyncRequests改成2时,helper里的内容就会分离成一个单独的bundle。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AByCnm32-1661777263265)(/tencent/api/attachments/s3/url?attachmentid=756507)]

splitChunks.maxInitialRequests

entry的最大并行请求数,默认是3个。这个选项主要适用于多页应用的项目

maxInitialRequest、maxAsyncRequests 、maxSize 、 minSize的优先级

maxInitialRequest/maxAsyncRequests < maxSize < minSize .

抽离案例

Vendors

chunk-a: react, react-dom, 一些组件1
chunk-b: react, react-dom, 一些组件2
chunk-c: angular, 一些组件3
chunk-d: angular, 一些组件4

webpack4 会自动创建两个vendor chunks,结果如下:
vendors~chunk-a~chunk-b: react, react-dom
vendors~chunk-c~chunk-d: angular
chunk-a 到 chunk-d: 只包含各自的组件

重复的vendors

chunk-a: react, react-dom, 一些组件1
chunk-b: react, react-dom, lodash, 一些组件2
chunk-c: react, react-dom, lodash, 一些组件3

webpack4同样也会创建两个vendors chunks,结果如下:
vendors~chunk-a~chunk-b~chunk-c: react, react-dom
vendors~chunk-b~chunk-c: lodash
chunk-a 到 chunk-c: 只只包含各自的组件

共享的modules

chunk-a: vue, 一些组件1, 一些被共享的业务组件(非node_modules资源)
chunk-b: vue, 另一些组件2, 一些被共享的业务组件
chunk-c: vue, 另一些组件3, 一些被共享的业务组件

假定被共享的组件大小超过30kb,那么webpack4会创建一个venrdors chunk和一个commons chunk,结果如下:
vendors~chunk-a~chunk-b~chunk-c: vue
comons-chunk-a~chunk-b~chunk-c: 一些被共享的业务组件
chunk-a 到 chunk-c: 只包含各自的组件
当被共享的业务组件大小小于30kb时,webpack4会将他们重复的模块放到chunk-a到chunk-c中,而不是分离出一个common chunk来。我们认为,下载一个体积小于30kb的chunk而导致额外的http请求,是不值得的。

多个分享的模块

chunk-a: react, react-dom, 一些组件1, 一些共享的react组件
chunk-b: react, react-dom, angular, 一些组件2
chunk-c: react, react-dom, angular, 一些组件3, 一些共享的react组件, 一些共享的angular组件
chunk-d: angular, 一些组件4, 一些共享的angular组件

webpack4会创建两个vendors chunks和两个commons chunks
vendors~chunk-a~chunk-b~chunk-c: react, react-dom
vendors~chunk-b~chunk-c~chunk-d: angular
commons~chunk-a~chunk-c: 一些共享的react组件
commons~chunk-c~chunk-d: 一些共享的angular组件
chunk-a to chunk-d: 各自的组件

配置解析

  • chunks:可用值为“all”、“async”、“initial”,默认值是“async”

    • all:拆分同步和异步文件
    • async:只对异步文件作处理
    • initial:只对入口文件进行拆分,不去处理异步文件里的模块
  • minSize:控制最小包的大小,大于这个值才会去拆分,如果拆分的公共模块小于这个大小,那就复制成多份,直接打包到引用该模块的包里

  • minChunks:模块的重复调用次数大于等于minChunks值时,就会满足这项拆包条件,但只看入口模块导入的,不看动态加载模块中导入的(import(‘…’)),即使设置的chunks为“all”。

  • maxInitialRequests:入口文件最大请求的文件数量(import等方式)

    • 入口文件本身算一个请求
    • 入口文件动态加载的模块不算在内
    • 通过runtimeChunk拆分出来的runtime文件不算在内
    • 只算js,css不算在内
    • 如果同时有两个模块满足cacheGroups的拆分规则,但maxInitialRequests只允许再拆分一个,那么会拆出体积更大的那个模块。
  • maxAsyncRequests:用于限制异步模块内部的并行最大请求数

    • import文件本身算一个请求
    • 只算js、css不算在内
    • 如果同时有两个模块满足cacheGroups的规则需要拆分,但maxAsyncRequests只允许拆分一个时,那么会拆出体积更大的那个模块。
  • name:主要用于分离chunks后的名字,可以是字符串或者函数,相同name会合并成一个chunk

  • splitChunks.cacheGroups

    • 可以继承或者重写splitChunks对象下的属性,但是test、priority和reuseExistingChunk只能配置在cacheGroup对象中
    • 每个添加的对象都有默认配置,如果想禁用此配置,可以将其设置为false
  • 5
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值