SplitChunksPlugin 插件快速入门

·  阅读 1990

前言

webpack v4 以前,使用 CommonsChunkPlugin 插件来提取公用的js文件和库,但是这个插件的功能有限,只能把公共文件打包到一个公共文件中,进入页面时加载,只能避免重复加载文件和库,并不能起到优化页面加载速度。

webpack v4版本推出后,官方把代码的分割工作交给了SplitChunksPlugin插件,这是一个内置的插件,无需额外引入,直接在optimization.splitChunks中书写配置即可。该插件不仅可以提取公共的文件和库,还可以提取按需加载的文件(即通过import('../a.js')动态引入的文件)。

官方推荐开发者将不需要立刻使用的代码(例如:登陆弹窗的代码,点击事件的代码)通过异步的方式引入,这样才是加快网页速度的关键。配合PrefetchingPreloading就更完美了,扯远了~~,这两个不在本文的讨论范围,有兴趣的朋友自行了解。

默认配置

官网给出的默认配置如下,即如果你不手动配置 soptimization.plitChunks ,webpack 将按照这个默认配置对代码进行分割操作。

module.exports = {
  //...
  optimization: {
    splitChunks: {
      chunks: 'async',
      minSize: 30000,
      maxSize: 0,
      minChunks: 1,
      maxAsyncRequests: 5,
      maxInitialRequests: 3,
      automaticNameDelimiter: '~',
      name: true,
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true
        }
      }
    }
  }
};
复制代码

默认配置的大概意思是:

  • 默认只对按需引入的模块进行代码分割;
  • 来自 node_modules 的模块,或被引用两次及以上的模块,才会做代码分割;
  • 被分割的模块必须大于30kb(代码压缩前);
  • 按需加载时,并行的请求数必须小于或等于5;
  • 初始页加载时,并行的请求数必须小于或等于3;

配置参数解读

解读配置前,先科普一下什么叫 "chunks" 吧,可能有些人有疑问,SplitChunksPlugin 中的chunks是什么东西,为什么要split它?

通俗的讲,"chunks" 是指交给webpack处理的文件,例如:js/css/img文件,通过import的方式引入的模块或文件都可以称之为"chunks"。

splitChunks.chunks

可选值: function (chunk) | initial | async | all

  • initial 表示入口文件中非动态引入的模块
  • all 表示所有模块
  • async 表示异步引入的模块

配置如下:

// src/index.js 
// 静态引入
import lodash from 'lodash' // 大小约529kb(压缩后)
// 动态引入
import(/*webpackChunkName:"jquery"*/ 'jquery') // 大小约281kb(压缩后)
复制代码
// webpack.config.js
module.exports = {
  //...
  optimization: {
    splitChunks: {
      chunks: 'async'
    }
  }
};
复制代码

配置为chunks: 'async'时(默认),打包结果如下:

原因分析:

只对动态引入的chunks做代码分割,这里只把jquery的代码单独打包到vendors~jquery.js中,lodash的代码没有单独打包出来,而是打包到index.js文件里了。

修改配置为chunks: 'initial',打包结果如下:

原因分析:

把动态引入和静态引入的代码都做了代码分割,分别将他们单独打包到独立的文件中。

修改配置为chunks: 'all',打包结果如下:

原因分析:

看上去的结果和initial的差不多,但还是有区别的,如果设置为all,会把静态和动态的模块代码都打包到一个chunk,这里之所以分开,是因为两个包都大于30kb,刚好命中minSize的规则。如果两个包的体积都小于30kb,那么他们将被打包成一个文件,此处不再演示,有兴趣的朋友自己试一下。

修改index.js文件如下:

// src/index.js 
import lodash from 'lodash'
复制代码

再次打包结果如下:

原因分析:

lodash被分割到venders~index.js中,因为lodash是在入口文件中直接引入的。

splitChunks.minSize

配置如下:

// src/index.js 
// 静态引入
import lodash from 'lodash' // 大小约529kb(压缩后)
复制代码
// webpack.config.js
module.exports = {
  //...
  optimization: {
    splitChunks: {
      chunks: 'all',
      minSize: 600000
    }
  }
};
复制代码

打包结果如下:

原因分析: minSize设置为600kb,但是lodash库的大小没有超过600kb,所以没有做代码分割。

splitChunks.maxSize

单个模块大小超过maxSize(且大于minSize)时,将进行代码分割,优先级 minSize > maxSize > (maxInitialRequests/maxAsyncRequests),0表示不尝试对超大体积的模块进行分割,如果设定一个值(默认值30000),那么当单个模块超出这个值时,就会尝试对这个模块分割,且忽略maxInitialRequests/maxAsyncRequests这个两个参数的限制。

splitChunks.minChunks

引用次数超过这个值时,才进行代码分割(动态引入除外)。

配置如下:

// src/index.js 
// 静态引入
import(/*webpackChunkName:"a"*/ './a.js')
import(/*webpackChunkName:"b"*/ './b.js')
复制代码
// src/a.js 
import 'jquery'
复制代码
// src/b.js 
import 'jquery'
复制代码
// webpack.config.js
module.exports = {
  //...
  optimization: {
    splitChunks: {
      chunks: 'all',
      minChunks:2
    }
  }
};
复制代码

打包结果如下:

原因分析: minChunks为2,a模块和b模块都引入了jquery,满足模块被引入两次的条件,所以对jquery做代码分割。

splitChunks.maxAsyncRequests

maxAsyncRequests表示经过splitChunks代码分割后,并行加载的异步chunk数不超过这个值。

配置如下:

// src/index.js 
// 静态引入
import(/*webpackChunkName:"a"*/ './a.js')
import(/*webpackChunkName:"b"*/ './b.js')
复制代码
// src/a.js 
import 'jquery'
复制代码
// src/b.js 
import 'jquery'
复制代码
// webpack.config.js
module.exports = {
  //...
  optimization: {
    splitChunks: {
      chunks: 'all',
      maxAsyncRequests: 1
    }
  }
};
复制代码

打包结果如下:

原因分析:

模块a和模块b都引入了jquery,但是jquery没有被单独打包成一个模块,因为如果单独打包的话,进入首页时,就会同时加载index.js和vendors~a~b.js。这样一来,就违反异步请求数不能大于2的限制。所以模块a和b会作为单独的js被引入到index.js中。打开index.html可以看到,只引入了index.js一个script标签。

splitChunks.maxInitialRequests

入口文件最多并行发出的异步请求数,3表示页面刚刚进来时,最多只请求3个js文件,所以入口页面的代码分割不会超过3个(即使单个文件可能很大)。 配置如下:

// src/index.js 
// 静态引入
import jquery from 'jquery'
import(/*webpackChunkName:"a"*/ './a.js')
复制代码
// src/a.js 
import 'jquery'
复制代码
// webpack.config.js
module.exports = {
  //...
  optimization: {
    splitChunks: {
      chunks: 'all',
      maxAsyncRequests: 1
    }
  }
};
复制代码

打包结果如下:

原因分析:

由于maxAsyncRequests设置为1,所以只有一个index.js被引入到index.html中。

如果将maxAsyncRequests设置为2,打包结果如下:

由于maxAsyncRequests设置为2,即index.html中,最多可以有2个script标签,所以jquery也被单独打包到wendors~index.js中了。

splitChunks.automaticNameDelimiter

chunk名(vender)和原始名之间的连接符,例如:vender~index.js、vender~a.js 中间的~号,就是通过这个参数来配置的。

splitChunks.name

可选值: boolean: true | function (module, chunks, cacheGroupKey) | string

用户指定分割模块的名字,设置为true表示根据chunks和cacheGroup key自动生成

splitChunks.cacheGroups

缓存组,做代码分割时,webpack会把一个模块在所有地方的引入情况做统计,最后形成一个引用图,这样才可以做到代码分割的优化(引用次数,是否被引用过,避免重复打包),这个组中,自动继承且可以覆盖 splitChunks.* 的配置,它还有自己的配置(test, priority,reuseExistingChunk,filename,enforce)

cacheGroups的配置如下:

module.exports = {
  //...
  optimization: {
    splitChunks: {
      // ...
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true
        }
      }
    }
  }
};
复制代码
  • 通过 cacheGroups,我们可以定义自定义 chunk 组,通过 test 条件对模块进行过滤,符合条件的模块分配到相同的组。

  • cacheGroups 有两个默认的组,一个是 vendors,匹配来自 node_modules 目录的模块;一个 default,包含了由两个以上的 chunk 所共享的模块。vendors的优先级高于default。

cacheGroups.{cacheGroup}.test属性

可选值:function (module, chunk) | RegExp | string 指定些模块属于venders这个缓存组,省略test表示所有模块

cacheGroups.{cacheGroup}.priority属性

一个模块可以属于多个缓存组,该属性指定了缓存组的优先级,默认为0,允许使用复数来指定优先级,例如:-10的优先级比-20的高

cacheGroups.{cacheGroup}.reuseExistingChunk属性

二次利用已经存在的chunk,如果这个缓存组中的chunk已经在入口模块(main module)中存在了,就不会引入,这就是cacheGroups缓存组存在的意义之一。

cacheGroups.{cacheGroup}.filename属性

用于指定匹配的模块名,如果不指定 filename属性的话,会使用默认的chunk名。

cacheGroups.{cacheGroup}.enforce属性

设置为true表示忽略 splitChunks.minSize、splitChunks.minChunks、splitChunks.maxAsyncRequests和splitChunks.maxInitialRequests的配置,为当前缓存组生成chunks。

例如,使用mini-css-extract-plugin插件打包css文件时,通常需要配置cacheGroups.{cacheGroup}.enforce属性。


module.exports = {
  //...
  optimization: {
    splitChunks: {
      // ...
      cacheGroups: {
        styles: {
          name: 'styles',
          test: /\.css$/,
          chunks: 'all',
          enforce: true,
        }
      }
    }
  }
};
  
复制代码

这样一来,mini-css-extract-plugin插件会自动把css样式分割到dist文件夹下。

总结

看到这里,相信你已经对SplitChunksPlugin插件有了一定的了解,如果你觉得本文对你有帮助,请不要吝啬你的赞美,给我点个赞吧!如果发现问题,也欢迎在评论区指出,我看到后会做修改的。

题外话

实不相瞒,这篇文章花费了将近一天的时间,因为我也是边学边总结的。上一次发文已经是半年前的事了,果然人都是懒惰的!希望以后自己可以多学习,多写文章。看文章容易,写文章难,因为不能按照官网的来,否则毫无意义,每个知识点都要自己亲自去验证,才敢发出来给大家看,因为不想误导了大家。

github链接: github.com/linlif/webp…

(本文完)

分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改