简单说一下 Webpack分包

发布于:2025-05-19 ⋅ 阅读:(18) ⋅ 点赞:(0)

最近在看有关webpack分包的知识,搜索了很多资料,感觉这一块很是迷惑,网上的资料讲的也迷迷糊糊,这里简单总结分享一下,也当个笔记。 如有错误请指出。

为什么需要分包

我们知道,webpack的作用,是将各种类型的资源,统一转换成浏览器可以直接识别的 Js Css Html 图片 等等,正如其官网图描述的一样

在这个过程中,webpack需要兼容各种模块化标准 (CommonJs & ESModule),将各种模块化标准实现的各个模块,统一打包成一个bundle.js文件,这个文件可以直接被script标签引用, 被浏览器直接识别。

 如果你了解webpack的实现原理,就可以知道,其打包结果本质上是一个自执行函数。 各个模块被封装成一个 路径 => 代码 的Map,通过入参传入。 自执行函数内部,有一套逻辑可以读取,执行这些模块代码,并且兼容统一了各种模块化标准。

说了这么多,webpack给我们的印象是一个 多 归 一 的构建工具,无论我们有多少的模块,最后输出的结果只有bundle.js一个。这就带来一个问题,如果我们的模块体积很大,其中可能还包含了图片等信息,那么从服务端将其下载到本地就需要很大的开销,可能导致用户浏览器出现长时间白屏的现象。我们需要将逻辑上一个大的bundle.js拆散,根据用户的需求,动态的下载,保证用户的体验。

完成这个拆分工作,就需要用到分包相关的技术。

分包的工作,通常由SplitChunksPlugin完成,其是webpack内部提供的一个插件,我们不需要额外的从NPM上下载这个插件,只需要在 optimization中配置splitChunks属性即可。

我们可以看到,SplitChunksPlugin是在optimize阶段通过钩入 optimizeChunks钩子实现分包的

chunks属性

很多介绍splitchunks的文章都是从 chunks开始介绍的,说这个属性有三个值

 async(默认)| initial | all 

分别对应,拆分异步模块,拆分同步模块,拆分同步和异步模块

由于默认值是 async 所以通过 import() 动态导入的模块会被单独拆分!

现象如此,但是这个理解完全是错误的!

webpack默认行为

首先你需要知道,webpack对异步模块的拆分是webpack的默认行为,和splitChunks无关

 我们很容易验证,设置splitChunk: false 关闭拆包,对于如下例子:

webpack.config.js
  // 单一入口
  entry: {
    main: "./src/main.js",
  },

main.js
// 通过 import动态导入add模块 
import("@utils/add").then((module) => {
  console.log(module.add(100, 200));
});

add.js
// 导出一个简单的 add 函数
export function add(x,y){
    return x + y
}


入口main.js动态引入了一个 add函数,由于关闭了拆包优化,最后应该只有一个js文件被输出,但是最终的结果为:

可以观察到,无论开不开分包优化,webpack对于异步导入的模块,都会将其单独作为一个chunk拆分出去,这样也确保了请求资源的时候不把异步模块同主模块一同加载进来,这个是默认行为

add.js
import './commonUtils'
export function add(x,y){
    return x + y
}

我们改动一下,在add.js中同步引入 commonUtils模块,打包结果如下:

可以发现,commonUtils模块被打入了 add.js一起。

对于webpack来说,被异步模块引入的模块,如果这个模块没有被同步模块引用过,那么在异步模块中无论如何被引用,都是异步模块。

如果我们在 main.js中同步引入 commonUtils

main.js
import '@utils/commonUtils'
import("@utils/add").then((module) => {
  console.log(module.add(100, 200));
});

可以看到,最终的打包结果中,commonUtils被和main这个同步模块打包到一起了,如下:

 其实这也好理解,如果一个模块都已经被同步载入过了,那么就没必要再费网络资源去异步请求了,直接复用即可。

虽然同步模块和异步模块在默认状态下可以直接复用,但是这种复用仅仅存在于 同一入口的情况下,看下面例子: 我们增加一个app模块,在app中异步引入commonUtils模块,在main中同步引入commonUtils模块 

webpack.config.js
  entry: {
    app: "./src/app.js",
    main: "./src/main.js",
  },

app.js
import("@utils/commonUtils").then((module) => {
  console.log(module);
});


main.js
import '@utils/commonUtils'

可以看到,在多入口的情况下,commonUtils被打包了两次 

为什么多入口之间不能复用,因为webpack要保证每一个入口都是独立可用的,对于main.js 其单独使用的时候如果要获取commonUtils.js 需要将整个main.js完整下载下来,无法保证每个入口的独立性,所以会单独打包一份出来。

我们把main.js中的同步引入commonUtils也改成异步,可以发现最终结果中只有一个commonUtils模块,也就是说异步模块在不同入口之间是可以复用的

这也符合逻辑,对于每个入口,由于对于commonUtils的载入都是异步的,在一开始载入的时候都不需要同步载入commonUtils,所以自然使用一个独立模块即可。 

 说完了这些,我们做个小总结 避免混乱

首先,这些都是在不进行任何分包优化下的情况,一定要明确前提

  1. webpack在默认情况下,会 并且 仅仅会对异步载入的模块进行独立分包处理
  2. 对于同步的模块,webpack默认情况下都会打入主包中同步引入,不会独立拆包
  3. 异步模块中无论同步,还是异步的模块引入 都算成是异步引入 
  4. 在相同入口的情况下,同步引入和异步引入会共享同步引入的模块,不会单独异步拆包
  5. 在不同入口下,同步和异步引入相对独立,无法共用,会打两份,但是异步模块之间可以互相共用。

我认为,了解webpack默认行为会对后面的学习减少很多的"弯路",在理解分包现象的时候需要理解到底是splitChunk的优化结果,还是webpack的默认行为。

比方说chunks: async虽然是默认值,但是造成分包的并不是这个属性的作用,属于webpack的默认行为!

chunks属性的真正含义

chunks属性的真正含义如下

  • async: 仅仅对异步导入的模块进行splitChunks优化,同步导入的模块会被忽略,也是chunks的默认值 (默认情况下,splitChunks只对webpack默认拆分出来的异步模块生效)
  • initial: initial即初始化的意思,浏览器初始化加载模块的时候,会将引用的所有同步模块都载入,所以initial的含义是,对于同步导入的模块(即 初始化阶段会被载入的模块) 进行splitChunks优化,对于异步导入的模块,会被忽略。 我们知道webpack的默认状态下不会对同步模块单独拆包,所以initial就是用来增强其处理同步模块的功能

浏览器初始化的时候,会将同步模块一口气都载入,那么分包的意义何在呢?

其实主要目的是对于打包多入口时,在默认情况下,如果两个如果都引用了某个同步模块, 为了保证两个入口的独立性,这个模块会被重复两次打包。initial的作用就是可以把这个共同引用的同步模块拆分出来,减小打包文件的总体积。

  • all: 对于同步,异步引入的模块,都会进行splitChunks的优化,这个也是被官方推荐的方式。

很多教程中,说把chunks设置为 all 的作用是除了拆分async 还能拆分第三方vendor(即 node_modules)模块 这个也是不对的,只是all很强大,开启之后可以达到拆分第三方模块的效果,但是绝对不是其作用就是为了拆分第三方模块。 具体实现原理下面会说

我们看一些例子,如下,我们简单的设置splitChunks的chunks: async 为了不影响我们观察打包结果,我们先将minSize minChunks分别设置为 0 和 1 也就是让拆包大小和引用chunks数不影响chunks本身的效果。

// webpack.config.js
   
  entry: {
    app1: "./src/app1.js",
    app: "./src/app.js",
    main: "./src/main.js",
  },

    splitChunks: {
      chunks: "async",
      minSize: 0,
      minChunks: 1,
    }

// app1.js
import"@utils/commonUtils"

// app.js
import("@utils/commonUtils").then((module) => {
  console.log(module);
});

// main.js
import"@utils/commonUtils"

未开启splitChunks优化

开启 chunks: async  优化异步模块

可以看到,打包结果和不设置 splitChunk没什么两样,但是这并不代表其未生效,我们来分析一下

首先,在进行拆包优化之前,由于在三个入口分别同步和异步引入了 commonUtils模块。 由于在两个不同入口中,同步异步引用无法复用,webpack的默认行为会把异步的commonUtils拆分出来,并且把同步引入的commonUtils合并到主包中。

优化开始,由于chunks: async 此时只对拆分出来的commonUtils进行拆分,忽略同步的commonUtils 所以此时没什么可优化拆分的,splitChunks不会对打包结果有什么改变。

我们把chunks改成 initial 即 只对同步模块进行优化, 可以看到,此时,两个commonUtils被拆分出来了,由于有两个入口同步引用了commonUtils,initial模式下,会把这个同步模块单独拆分进行复用。但是由于initial模式会忽略异步模块,所以会导致异步的commonUtils和同步的commonUtils无法复用。

为了解决同步异步的commonUtils不能复用的问题,我们把chunks设置为 all 即 对同步引入和异步引入的模块都开启分包优化,此时的打包结果如下:

可以看到,由于对于同步异步的模块splitChunk都会识别处理,所以commonUtils会被单独拆分成一个chunk,供同步和异步的引入使用,这样就达到了对commonUtils的最小化拆分。

再看一个例子,我们引入两个入口,其中:

app1.js引入一个node_modules下的 lodash模块
main中引入我们自己定义的 utils/commonUtils.js模块

webpack.config.js
entry: {
    app1: "./src/app1.js",
    main: "./src/main.js",
  },

chunks: all

// app1.js
import "lodash";

// main.js
import"@utils/commonUtils"

打包结果如下:

你会发现,同样是被一个入口引用1次,为什么lodash就被单独拆分了,我们自己定义的包就没被拆分,而我们已经设置了minSize: 0 也就是模块大小不会影响拆包,那么为什么第三方模块就被拆分了,我们自定义的模块就不会? 这个就要引入我们下面要说的 cacheGroup 缓存组的概念了  

cacheGroups缓存组

缓存组用来定义哪些模块被拆分成一组,拆分成一组的模块可以被浏览器单独的缓存,不用在每次更新的时候重新从服务器获取。

cacheGroups需要配置一个对象,key未缓存组的名称,value为一个配置对象,其中使用 test属性匹配模块的名称,我们举个例子:

  splitChunks: {
      chunks: "all",
      minSize: 0,
      minChunks: 1,
      cacheGroups: {
        MyUtils: {
          test: /\/utils\//,
          minSize: 0,
          minChunks: 1,
          name: 'MyUtils'
        }
      }
}

上述例子,我们设置一个MyUtils缓存组,其中test匹配路径中包含 utils的模块,并且设置其单独分包名称为 MyUtils。 其含义是,需要webpack对路径中包含 utils的模块单独拆出来并且合并到一个包里,这个包的名称为 MyUtils.

可以看到,这时我们自己定义的 utils/commonUtils.js被单独打包了。

那么回到上面的问题,为什么node_modules的模块就会被单独拆分,我们自己写的模块就需要我们自定义一个缓存组才能实现拆分? 这是因为webpack内置了两个 defaultVendors和default两个缓存组 如下:
 

module.exports = {
  //...
  optimization: {
    splitChunks: {
      chunks: 'async', // 代码分割的初始块类型,默认为异步加载的模块
      minSize: 20000, // 生成 chunk 的最小体积(以字节为单位)
      minRemainingSize: 0, // Webpack 5 新增,确保拆分后剩余的最小大小
      maxSize: 0, // 尝试将大于 maxSize 字节的 chunk 拆分为较小的部分
      minChunks: 1, // 共享该模块的最小 chunks 数
      maxAsyncRequests: 30, // 按需加载时的最大并行请求数
      maxInitialRequests: 30, // 入口点的最大并行请求数
      automaticNameDelimiter: '~', // 生成名称时使用的分隔符
      enforceSizeThreshold: 50000, // Webpack 5 新增,强制执行拆分的体积阈值
      cacheGroups: { // 缓存组配置
        defaultVendors: {
          test: /[\\/]node_modules[\\/]/, // 匹配 node_modules 中的模块
          priority: -10, // 优先级
          reuseExistingChunk: true // 如果当前 chunk 包含已从主 bundle 中拆分出的模块,则重用该模块
        },
        default: {
          minChunks: 2, // 被至少两个 chunks 共享的模块
          priority: -20, // 优先级低于 vendors
          reuseExistingChunk: true // 重用已存在的 chunk
        }
      }
    }
  }
};

可以看到,对于路径包含 /node_modules/的模块,splitChunks插件会默认将其分到一个缓存组中去,而对于default缓存组,其没有设置test属性,意味着没有匹配到任何缓存组的模块都会被default缓存组匹配,其优先级priority: -20 低于defaultVendors的-10 为最低优先级的缓存组,可以被当成是一组兜底的缓存策略。

这也就解释了为什么我们自己定义的commonUtils模块没有被单独拆包,虽然其匹配到了默认的default缓存组,但是由于其内部配置了 minChunks: 2 也就是说当前模块必须被至少两个chunk匹配到,才会单独拆包,而上述例子commUtils只被引用了一次,所以没有被单独划分。

你也许会问? lodash也只被匹配了一次啊? 为什么也被单独拆包了? 这时因为defaultVendors缓存组没有配置minChunks,那么会直接使用顶层的minChunks配置,也就是minChunks:1 任何模块只要被一个chunk引用,就会被单独拆包 (注意这里是chunks 不是 modules)

注意,cacheGroups这个名字很容易误导人,其并不是把test匹配到的模块都合并到一个chunk,还要考虑其中的 minSize minChunks name等等属性的作用

你可以将其理解为 缓存规则组 把一组决定模块是否缓存 如何缓存的规则 放到一个组里面,test匹配到某个group就使用这个group的规则。

 cacheGroups的作用原理 & 如何禁用cacheGroups

 回忆一下我们上面的例子,当我们设置cacheGroups时,如果cacheGroups的名称和默认的 defaultVendors / default 不重复,那么不会覆盖默认的cacheGroups配置

  splitChunks: {
      chunks: "all",
      minSize: 0,
      minChunks: 1,
      cacheGroups: {
        MyUtils: {
          test: /\/utils\//,
          minSize: 0,
          minChunks: 1,
          name: 'MyUtils'
        }
      }
}

等价于 

  splitChunks: {
      chunks: "all",
      minSize: 0,
      minChunks: 1,
      cacheGroups: {
        MyUtils: {
          test: /\/utils\//,
          minSize: 0,
          minChunks: 1,
          name: 'MyUtils'
        },
        defaultVendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10,
          reuseExistingChunk: true 
        },
        default: {
          minChunks: 2, 
          priority: -20, 
          reuseExistingChunk: true 
        }
      }
}

所以,如果我们不想使用默认配置,不能直接 cacheGroups: {} 而是需要

// 关闭默认cacheGroups
      cacheGroups: {
        default: false,
        defaultVendors: false
      }

禁用缓存组之后,我们再来看分包结果

可以看到,lodash和commonUtils都没有被拆分。你也许会疑问了? 即便什么缓存组都没匹配到 那顶层的默认配置不是还写着 minChunks:1 呢么?

 

你可以简单的理解为,cacheGroups是拆包的 “引擎” 只有匹配到了cacheGroups才会启动分包流程,如果任何的缓存组都没匹配到,光有顶层的那些配置是没有任何意义的。

所以你需要记住,在没有匹配到任何缓存组的情况下,splitChunk不会对模块进行拆包优化!

所以,回到前面的问题,有些博客说,把chunks设置为all就开启了对第三方模块的分包优化 这是不严谨的,很可能让人误认为拆分第三方模块是chunks all的效果,但是其实不是的。

由于我们引用第三方模块大多是同步引入,而chunks默认值为 async,所以默认情况下,我们不会对同步引入的第三方模块进行分包优化。

当开启了all的时候,第三方模块也会被纳入分包优化的范围,而splitChunks又内置了defaultVendors的缓存组,并且没有设置其minChunks

所以才会拆分出第三方内容,设置chunks: all仅仅是整个拆分流程中的一个环节而已。

minSize

设置chunk的最小尺寸,单位字节(Byte),只有在模块尺寸大于这个属性值的时候,才会拆包

commonUtils的尺寸约为40B 当设置顶层minSize: 40 / 50 时,拆分结果如图

 同时,我们还可以在缓存组的内部设置minSize,其会覆盖顶层的minSize配置

minChunks 

minChunks限制了模块的最小chunks引用数,也就是模块被至少 minChunks 个chunk引用,才会被分割出去。

这里的chunk计数,取决于chunks的配置,如果chunks配置为 async 那么只有异步模块会进来匹配这个minChunks 如果是inital 那么只有同步模块会被计算进chunks 如果是all则都被计算。我们看例子

webpack.config.js
    splitChunks: {
      chunks: "initial",
      minSize: 0,
      minChunks: 1,
      cacheGroups: {
        // 关闭默认缓存组
        defaultVendors: false,
        default: false,
        utils: {
          test: /\/utils\/commonUtils.js/,
          minChunks: 3,
          name: 'MY_UTILS'
        }
      }
}

  entry: {
    app1: "./src/app1.js",
    app: "./src/app.js",
    main: "./src/main.js",
  },


// main.js
import"@utils/commonUtils"

//app.js
import("@utils/commonUtils").then((module) => {
  console.log(module);
});

// app1.js
import"@utils/commonUtils"

main和app1 同步引用了commonUtils模块,app异步引入commonUtils

我们设置commonUtils的缓存组minChunks: 3 chunks: initial 此时打包如下

可以看到 commonUtils被打包了3份 为什么?

1. webpack默认情况下,会把异步app.js中的common模块单独拆分

2. 由于chunks为initial 所以只会统计同步引入common模块的chunks数 只有app和main两个,所以不满足minChunks: 3的配置,不分包,会对app1 main 两个chunk都生成一份commonUtils

当我们设置minChunks: 2

可以看到,main和app1中的common模块被抽取出来了,但是由于chunks为initial 没有app中异步分离出来的commonUtils复用。 由于异步模块会被忽略,splitChunks不知道有一份可以被复用的commonUtils已经被生成了。

为了解决,我们设置chunks为all,并且设置minChunks:3 如下:

    splitChunks: {
      chunks: "all",
      minSize: 0,
      minChunks: 1,
      cacheGroups: {
        defaultVendors: false,
        default: false,
        utils: {
          test: /\/utils\/commonUtils.js/,
          minChunks: 3,
        },
      },}

此时,只有一份common被抽取,如下:

其过程为
1. webpack默认抽取app中的异步common模块

2. 分包优化的时候 由于同步异步模块都会被记入minChunks统计,满足minChunks: 3的条件 所以commonUtils会被单独拆包,但是发现webpack默认已经拆了一份common出来 就直接复用即可。

而对于chunks initial的情况下,由于忽略了异步的common模块,所以无法对已经拆出来的模块进行复用。

enforce 强制拆分

enforce通常用在缓存组内,如果某个缓存组内设置了enforce: true 那么这个缓存组会忽略全局的 minSize minChunks等等这些限制,仅以缓存组内部的配置为准
 

需要注意的是 enforce是用来忽略全局的配置的,无法忽略缓存组内部的配置

看以下例子

// webpack.config.js
    splitChunks: {
      chunks: "all",
      minSize: 999999,
      minChunks: 999999,
      cacheGroups: {
        defaultVendors: false,
        default: false,
        utils: {
          test: /\/utils\/commonUtils.js/,
        },
      },
}

// 入口
import"@utils/commonUtils"

由于外层的 minChunks minSize设置的非常大,所以打包出来的结果是不进行任何拆分 

此时,如果我们设置了enforce: true 如下

   splitChunks: {
      chunks: "all",
      minSize: 999999,
      minChunks: 999999999,
      cacheGroups: {
        defaultVendors: false,
        default: false,
        utils: {
          test: /\/utils\/commonUtils.js/,
          enforce: true,
        },
      },

 外层的配置将会被直接忽略,可以看到 common模块还是被正常分离出来了。这就是enforce直接忽略了外层的配置。 

但是,如果我们对缓存组内部设置minChunks: 2 此时enforce就无法对内部的限制产生效果了。

所以,当你想忽略掉顶层配置强制拆分某个模块的时候,可以尝试使用enforce属性! 

maxInitialRequests

maxInitialRequest代表最大同步并发数量,也就是限制了单个entry下加载的最大initial chunk数量

需要注意 maxInitialRequests 默认包含入口chunk,当对某个缓存组做更精细化的配置时,要减去1

在看例子之前,需要先将全局的 enforceSizeThreshold设置为0,即没有强制尺寸分包上限,方便我们观察
 

webpack.config.js
   
 splitChunks: {
      chunks: "all",
      minSize: 0,
      minChunks: 1,
      maxInitialRequests: 1,
      enforceSizeThreshold: 0,
      cacheGroups: {
        defaultVendors: false,
        default: false,
        react: {
          test: /react/,
          minSize: 0,
          minChunks: 1,
        }
     }

// 入口 
import react from 'react'

maxInitialRequest默认包含了入口chunk,所以在某个缓存组内分析时要先减去入口chunk 
比如对于react缓存组,其没设置maxInitialRequests 所以继承全局的为 1 也就是说,对于react缓存组,其最多可以分出 maxInitialRequest - 入口chunk(1) = 0 个chunk,也就是说react不能被单独分包,哪怕满足minSize minChunks这些, 最终分包结果如下:

可以看到,react收到maxInitialRequests的限制,被合并到了入口chunk中。

若修改react缓存组内的maxInitialRequests: 2 那么其会覆盖全局的配置,将react单独分包 如下:
 

如果无法满足 maxInitialRequest的要求,那么会尽可能把大的模块拆分出来,小的合并,看下面例子
 

// webpack.config.js
    splitChunks: {
      chunks: "all",
      minSize: 0,
      minChunks: 1,
      maxInitialRequests: 2,
      enforceSizeThreshold: 0,
}

entry: {
    app1: "./src/app1.js",
    app: "./src/app.js",
    main: "./src/main.js",
}

// app.js
import 'lodash'


// app1.js
import 'lodash'
import 'react'

//main.js
import"react"

其引用关系如下图:

可以看到,app1模块的initialRequest为 = app1入口chunk + lodash + react = 3 不满足 maxInitialRequest = 2


webpack会把其中更大的lodash模块拆分出来,更小的react模块合并到入口chunk内, 如下
可以看到,对于 app 和 main 由于只引用了一个模块 满足maxInitialRequest: 2
但是对于 app1 更大的lodash被拆出来共用 ,更小的react被合并进app入口了。

 

注意!在 webpack 中,runtimeChunk: true不包含在 maxInitialRequests 的限制内。 

maxAsyncRequest

maxAsyncRequest和maxInitialRequest类似,区别在于,其限制的是 对于每个 import() 动态导入 最大并发的可以下载的模块数, 其中 import() 本身算一个并行请求

有点绕,举个例子
我们在入口动态引入一个 testModule.js 其中同步引入add sub两个模块 如下

// main.js
import ('@utils/testModule')

// testModule.js
import "./add";
import "./sub";


// add.js
export function add(x,y){
    return x + y
}

//sub.js
export function sub(x,y){
    return x - y
}

其依赖图为

我们设置缓存组,让各个模块之间都相互独立

    splitChunks: {
      chunks: "all",
      minSize: 0,
      minChunks: 1,
      enforceSizeThreshold: 0,
      cacheGroups: {
        utils: {
          test: /\/utils\//,
          minChunks: 1,
          minSize: 0,
          priority: 10,
          name: ()=>'UTILS'+Math.random()
        },
      },

 打包结果如下

全局设置 maxAsyncRequests: 1  结果如下:

为什么三个模块被打包到一起了? 因为在main中动态引入testModule 由于需要满足maxAsyncRequest 此时 如果把add sub单独拆分,那么asyncRequest = 动态导入testModule + add + sub  = 3 不满足限制。

maxAsyncRequest设置为 2 可以看到打包结果为

add和sub被单独拆分,和testModule分离,此时的asyncRequest刚好为 2
设置maxAsyncRequests = 3 可以看到三个模块都被独立分割了

  

我们修改 testModule 动态引入add 并且设置maxAsyncRequests为1

// testModule.js
import("./add");
import "./sub";

splitChunks: {
      chunks: "all",
      minSize: 0,
      minChunks: 1,
      maxAsyncRequests: 1,
      enforceSizeThreshold: 0,
      cacheGroups: {
        utils: {
          test: /\/utils\//,
          minChunks: 1,
          minSize: 0,
          priority: 10,
          name: ()=>'UTILS'+Math.random()
        },
      },

此时可以看到,结果为

为什么add被单独拆分了? 这是不是不满足maxAsyncRequest: 1 的限制了? 其实不是

我们前面说了,splitChunks无法改变webpack的默认行为,由于add模块属于异步引入,对其拆分成一个单独的模块属于webpack的默认行为,所以splitChunks只能在add被拆分的基础上进行限制。

但是请你仔细考虑,maxAsyncRequests本身是不是就不包括异步引入的模块呢?

对于一个import动态引入的模块 其形式为 

import SyncModuleA. // 预解析
import SyncModuleB // 预解析
import (AsyncModuleC) // 运行时

对于同步引入的SyncModuleA & B 在整个模块被import的时候就会同步的下载这两个模块,但是对于动态引入的ModuleC 只有在运行到此的时候才会真正去加载,由此可见,异步引入模块是不包含在maxAsyncRequest中的 其本意是 在动态引入一个模块时,同步加载进来的模块数量,而其中的异步模块,并不属于 "同步加载进来的"  

我们继续修改maxAsyncRequest = 2 你可以猜到打包结果了

 其中,add.js是webpack默认行为拆分的,不包含在maxAsyncRequests的计算中,所以maxAsyncRequests = import(TestModule) + import 'add.js' = 2 满足约束

优化建议maxSize

maxSize表示,拆分的chunk最大值,当拆分出的chunk超过这个值的时候,webpack会尝试将其拆分成更小的chunks

maxSize是个 "建议性质"的限制,也就是webpack如果没有找到合适的切分点,maxSize将不起作用, 官方描述如下

maxSize的优先级 高于maxAsyncRequest / maxInitialRequest
看一个例子,

// main.js
import'react'

//webpack.config.js
optimization: {
    runtimeChunk: true,
    // splitChunks: false,
    splitChunks: {
      chunks: "all",
      minSize: 0,
      minChunks: 1,
      maxInitialRequests: 1,
      maxSize: 200,
      enforceSizeThreshold: 0,
}}

此时,react依旧会被拆分成小块,即便不满足maxInitialRequests,如下:

 


网站公告

今日签到

点亮在社区的每一天
去签到