代码拆分的四种方式


  1. 入口起点
  2. 入口依赖(dependOn)
  3. SplitChunksPlugin
  4. 动态导入

在项目中常见的四种文件


  1. xxx.bundle.js
  2. xxx.vendros.js
  3. xxx.chunk.js
  4. runtime

代码拆分

入口起点bundle


  1. entry:{
  2. main:"./src/main.js",
  3. index:"./src/index.js"
  4. },
  5. output:{
  6. path:path.resolve(__dirname,'dist'),
  7. filename:"[name].bundle.js"
  8. }

image.png

dependOnbundle


  1. entry:{
  2. main:{
  3. import:"./src/main.js",
  4. dependOn:"shared"
  5. },
  6. index:{
  7. import:"./src/index.js",
  8. dependOn:"shared"
  9. },
  10. shared:['lodash','axios']
  11. },
  12. output:{
  13. path:path.resolve(__dirname,'dist'),
  14. filename:"[name].bundle.js"
  15. }

  1. entry:{
  2. main:{
  3. import:"./src/main.js",
  4. dependOn:["shared","lodash"]
  5. },
  6. index:{
  7. import:"./src/index.js",
  8. dependOn:"shared"
  9. },
  10. shared:"shared"
  11. lodash:"lodash"
  12. },
  13. output:{
  14. path:path.resolve(__dirname,'dist'),
  15. filename:"[name].bundle.js"
  16. }

image.png

splitChunksvendors


可以将node_modules中的代码单独打包一个chunk
多入口中可以自动分析入口文件中有没有公共的依赖,如果有会打包成一个文件

  1. module.exports = {
  2. //...
  3. optimization: {
  4. splitChunks: {
  5. //在cacheGroups外层的属性设定适用于所有缓存组,不过每个缓存组内部可以重设这些属性
  6. chunks: "async", //将什么类型的代码块用于分割,三选一: "initial":入口代码块 | "all":全部 | "async":按需加载的代码块
  7. minSize: 30000, //大小超过30kb的模块才会被提取
  8. maxSize: 0, //只是提示,可以被违反,会尽量将chunk分的比maxSize小,当设为0代表能分则分,分不了不会强制
  9. minChunks: 1, //某个模块至少被多少代码块引用,才会被提取成新的chunk
  10. maxAsyncRequests: 5, //分割后,按需加载的代码块最多允许的并行请求数,在webpack5里默认值变为6
  11. maxInitialRequests: 3, //分割后,入口代码块最多允许的并行请求数,在webpack5里默认值变为4
  12. automaticNameDelimiter: "~", //代码块命名分割符
  13. name: true, //每个缓存组打包得到的代码块的名称
  14. cacheGroups: {
  15. vendors: {
  16. test: /[\\/]node_modules[\\/]/, //匹配node_modules中的模块
  17. priority: -10, //优先级,当模块同时命中多个缓存组的规则时,分配到优先级高的缓存组
  18. filename:'[id]_vendors.js'
  19. },
  20. default: {
  21. minChunks: 2, //覆盖外层的全局属性
  22. priority: -20,
  23. reuseExistingChunk: true, //是否复用已经从原代码块中分割出来的模块
  24. },
  25. },
  26. },
  27. },
  28. };

chunkIds

选项值 描述
‘natural’ 按使用顺序的数字 id。
‘named’ 对调试更友好的可读的 id。
‘deterministic’ 在不同的编译中不变的短数字 id。有益于长期缓存。在生产模式中会默认开启。
‘size’ 专注于让初始下载包大小更小的数字 id。
‘total-size’ 专注于让总下载包大小更小的数字 id。
  1. chunkIds: 'deterministic',

提取所有的资源到一个文件中

创建一个 commons chunk,其中包括入口(entry points)之间所有共享的代码。

  1. module.exports = {
  2. //...
  3. optimization: {
  4. splitChunks: {
  5. cacheGroups: {
  6. commons: {
  7. name: 'commons', // 值得是这个name
  8. chunks: 'initial',
  9. minChunks: 2,
  10. },
  11. },
  12. },
  13. },
  14. };
  15. W

此配置可以扩大你的初始 bundles,建议在不需要立即使用模块时使用动态导入

基于入口提取

  1. const path = require("path");
  2. const MiniCssExtractPlugin = require("mini-css-extract-plugin");
  3. module.exports = {
  4. entry: {
  5. foo: path.resolve(__dirname, "src/foo"),
  6. bar: path.resolve(__dirname, "src/bar"),
  7. },
  8. optimization: {
  9. splitChunks: {
  10. cacheGroups: {
  11. fooStyles: {
  12. type: "css/mini-extract",
  13. name: "styles_foo",
  14. chunks: (chunk) => {
  15. return chunk.name === "foo";
  16. },
  17. enforce: true,
  18. },
  19. barStyles: {
  20. type: "css/mini-extract",
  21. name: "styles_bar",
  22. chunks: (chunk) => {
  23. return chunk.name === "bar";
  24. },
  25. enforce: true,
  26. },
  27. },
  28. },
  29. },
  30. plugins: [
  31. new MiniCssExtractPlugin({
  32. filename: "[name].css",
  33. }),
  34. ],
  35. module: {
  36. rules: [
  37. {
  38. test: /\.css$/,
  39. use: [MiniCssExtractPlugin.loader, "css-loader"],
  40. },
  41. ],
  42. },
  43. };

懒加载chunk

import 动态导入语法(目前浏览器没有原生支持,需要babel)(???我在使用中并没有发现报错,也没用到syntax-dynamic-import)

依赖

  1. yarn add @babel/plugin-syntax-dynamic-import --save-dev

babel.config.js

  1. {
  2. "presets": [
  3. [
  4. "@babel/preset-env",
  5. {
  6. "useBuiltIns": "entry",
  7. "corejs": {
  8. "version": 3,
  9. "proposals": false
  10. }
  11. }
  12. ]
  13. ],
  14. "plugins":['@babel/plugin-syntax-dynamic']
  15. }
  1. import(/* webpackChunkName:'test' */"./test").then().catch()

webpack.config.js

chunkFilename

此选项决定了非初始(non-initial)chunk 文件的名称,就是懒加载的文件
管理懒加载进入的文件名字,如果没有添加webpack魔法名,此时的name和id一样,如果添加了webpackChunkName,那么name就是webpackChunkName

  1. output:{
  2. chunkFilename:'chunk/chunk_[name].js'
  3. }

runtimeruntime

注意

如果不提供runtimeChunk模块,那么公共chunk改变还是会影响到chunks,会导致缓存失效

  1. optimization:{
  2. // 将当前模块的记录其他模块的hash单独打包一个文件的runtime
  3. runtimeChunk:{
  4. name:entryPoint=>`runtime-${entryPoint.name}`
  5. }
  6. }

runtime运行时文件就是,import懒加载的文件会有一些引入之类的路径打入到bundle文件内,基于contentHas的缓存,当懒加载的文件变化的时候,has会变化,导致bundle文件的has也发生了改变
所以开启runtimeChunnk就能够提取runtime文件,保证bundle文件不改变
两个属性值

  • true 或 ‘multiple’
  • single

分别是如下两个配置的别名

  1. // multiple
  2. // 给每个文件打包出runtime文件
  3. module.exports = {
  4. //...
  5. optimization: {
  6. runtimeChunk: {
  7. name: (entrypoint) => `runtime~${entrypoint.name}`,
  8. },
  9. },
  10. };
  11. // single
  12. // 引入相同的文件会共享一个runtime
  13. module.exports = {
  14. //...
  15. optimization: {
  16. runtimeChunk: {
  17. name: 'runtime',
  18. },
  19. },
  20. };