浅谈webpack SplitChunksPlugin实用指南


Posted in Javascript onSeptember 17, 2018

提到前端打包工具,毫无疑问想先到的是webpack。但是前端发展地很快,时不时会有新东西出现,打包工具这边之前也出现parcel和rollup。各种工具的碰撞,相互汲取优点,促进技术的发展。

webpack4中支持了零配置的特性,同时对块打包也做了优化, CommonsChunkPlugin 已经被移除了,现在是使用 optimization.splitChunks 代替。

下面就开始介绍splitChunks的内容。

默认情况

首先webpack会根据下述条件自动进行代码块分割:

  • 新代码块可以被共享引用,或者这些模块都是来自node_modules文件夹里面
  • 新代码块大于30kb(min+gziped之前的体积)
  • 按需加载的代码块,并行请求最大数量应该小于或者等于5
  • 初始加载的代码块,并行请求最大数量应该小于或等于3

块打包默认情况下只会影响按需加载模块,因为对初始块也进行优化打包会影响HTML中的script标签数,增加请求数。

接下来看些例子来理解默认情况的打包。

模块全部是同步引入

// indexA.js
import React from 'react'
import ReactDOM from 'react-dom'
import _ from 'lodash'

console.log(_.join(['a', 'b'], '~'))

ReactDOM.render(
 <div>SplitChunk</div>,
 document.getElementById('root')
)

浅谈webpack SplitChunksPlugin实用指南

默认情况只会影响按需加载模块,所以所有内容全部被打包到一起了。

有模块动态导入

这里首先使用符合ECMAScript 提案 的 import() 语法

// indexA.js
import React from 'react'
import ReactDOM from 'react-dom'
import _ from 'lodash'
import(/* webpackChunkName: "async-jquery" */ 'jquery').then(component => {
 console.log(component)
})

console.log(_.join(['a', 'b'], '~'))

ReactDOM.render(
 <div>SplitChunk</div>,
 document.getElementById('root')
)

浅谈webpack SplitChunksPlugin实用指南

这里jquery使用动态导入,打包结果中可以看到jquery被单独打包了

react按需加载

同样的我们试要react按需加载,使用react-router提供的按需加载方案

AsyncModule模块按上面方案异步加载Dashboard

import React from 'react'
import ReactDOM from 'react-dom'
import {BrowserRouter, Route} from 'react-router-dom'
import AsyncModule from './AsyncModule.jsx'
import _ from 'lodash'
import $ from 'jquery'

console.log(_.join(['a', 'b'], '~'))

ReactDOM.render(
 <div>
 <BrowserRouter>
  <Route path='/' component={AsyncModule} />
 </BrowserRouter>
 </div>,
 document.getElementById('root')
)

浅谈webpack SplitChunksPlugin实用指南

从打包结果可以看到按需加载的模块被打包到0.js去了。

讲完了webpack默认情况下对打包块的优化,接下来看splitChunks配置项。

配置项

相关配置项:

module.exports = {
 //...
 optimization: {
 splitChunks: {
  chunks: 'async', 
  minSize: 30000,
  minChunks: 1,
  maxAsyncRequests: 5,
  maxInitialRequests: 3,
  automaticNameDelimiter: '~', 
  name: true,
  cacheGroups: {}
 }
 }
}
  • chunks: 表示哪些代码需要优化,有三个可选值:initial(初始块)、async(按需加载块)、all(全部块),默认为async
  • minSize: 表示在压缩前的最小模块大小,默认为30000
  • minChunks: 表示被引用次数,默认为1
  • maxAsyncRequests: 按需加载时候最大的并行请求数,默认为5
  • maxInitialRequests: 一个入口最大的并行请求数,默认为3
  • automaticNameDelimiter: 命名连接符
  • name: 拆分出来块的名字,默认由块名和hash值自动生成
  • cacheGroups: 缓存组。缓存组的属性除上面所有属性外,还有test, priority, reuseExistingChunk
    • test: 用于控制哪些模块被这个缓存组匹配到
    • priority: 缓存组打包的先后优先级
    • reuseExistingChunk: 如果当前代码块包含的模块已经有了,就不在产生一个新的代码块

配置项基本就上面这些,我们重点来看下chunks和cacheGroups。

chunks

chunks的取值是有initial, async, all。默认情况下是async,在本文第一部分已经介绍了它的表现,所以现在来看下其它两个的表现。

initial , all 模式会将所有来自node_modules的模块分配到一个叫vendors的缓存组;所有重复引用至少两次的代码,会被分配到default的缓存组。

// indexA.js
import './Dashboard.jsx'

// indexB.js
import './Dashboard.jsx'

// Dashboard.jsx
import React from 'react'
// webpack.config.js
splitChunks: {
 chunks: 'initial'
}

浅谈webpack SplitChunksPlugin实用指南

打包表现正如上面所述,产生了两个代码块vendors, default。

可以通过配置optimization.splitChunks.cacheGroups.default: false禁用default缓存组。

// webpack.config.js
splitChunks: {
 chunks: 'initial',
 cacheGroups: {
 default: false
 }
}

浅谈webpack SplitChunksPlugin实用指南

至于all和initial的差别,可以看下这篇文章Webpack 4 — Mysterious SplitChunks Plugin (要科学上网)

里面有提到 initial 模式下会分开优化打包异步和非异步模块。而 all 会把异步和非异步同时进行优化打包。也就是说moduleA在indexA中异步引入,indexB中同步引入, initial 下moduleA会出现在两个打包块中,而 all 只会出现一个。

cacheGroups

使用cacheGroups可以自定义配置打包块。

// indexA.js
import React from 'react'
import ReactDOM from 'react-dom'
import _ from 'lodash'
import $ from 'jquery'

// indexB.js
import React from 'react'
import ReactDOM from 'react-dom'
import('lodash')
import $ from 'jquery'

// webpack.config.js
optimization: {
 splitChunks: {
  cacheGroups: {
  commons: {
   name: 'commons',
   chunks: 'initial',
   minChunks: 2
  }
  }
 }
 }

浅谈webpack SplitChunksPlugin实用指南

根据开头介绍webapck分割条件,一些公共模块被打包进了commons,自定义打包块的优先级是0,所以现在公共模块会被打包进commons,而不是上述提到的默认打包块vendors(优先级为负)。

但是这边为什么lodash为什么没打包在一起呢?可以回顾下initial和all的区别。接下来实验下all的效果。

// indexA, indexB同上
// webpack.config.js
optimization: {
 splitChunks: {
  cacheGroups: {
   commons: {
    name: 'commons',
    chunks: 'all',
    minChunks: 2
   }
  }
 }
}

浅谈webpack SplitChunksPlugin实用指南

结果在预期中,lodash被打包在一起了。

提取第三方库

最后看下之前CommonsChunkPlugin常用的分离部分第三方库功能。这边你可以想一下怎么操作。

上面已经提到了设置 chunks: initial || all 都可以提取出第三方库。但是它是把所有第三库提取出来,所以我们在只提取react和react-dom的情况下,需要自定义一个cacheGroup。

// indexA.js
import React from 'react'
import ReactDOM from 'react-dom'
import _ from 'lodash'
import $ from 'jquery'

// webpack.config.js
entry: {
 indexA: path.join(__dirname, 'src/indexA.js')
},
optimization: {
 splitChunks: {
  chunks: 'all',
  cacheGroups: {
   vendors: {
    test: /react/,
    name: 'vendors'
   }
  }
 }
}

浅谈webpack SplitChunksPlugin实用指南

我们去重写了vendors打包块,只打包匹配react的模块,所以达到了之前CommonsChunkPlugin的功能。

或者

// index.jsx
import React from 'react'
import ReactDOM from 'react-dom'

// webpack.config.js
entry: {
 indexA: path.join(__dirname, 'src/indexA.js'),
 vendor: ["react", "react-dom"]
},
optimization: {
 splitChunks: {
  cacheGroups: {
   vendor: {
    name: "vendor",
    chunks: "initial"
   }
  }
 }
}

浅谈webpack SplitChunksPlugin实用指南

optimization.runtimeChunk

最后提一下runtimeChunk,通过 optimization.runtimeChunk: true 选项,webpack会添加一个只包含运行时(runtime)额外代码块到每一个入口。(译注:这个需要看场景使用,会导致每个入口都加载多一份运行时代码)

总结

webpack4的splitChunks功能是比较强大的,不过推荐还是使用默认模式,或者提取一下第三方库。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
JS+CSS制作DIV层可(最小化/拖拽/排序)功能实现代码
Feb 25 Javascript
JQuery判断子iframe何时加载完成解决方案
Aug 20 Javascript
简单的分页代码js实现
May 17 Javascript
jQuery插件 Jqplot图表实例
Jun 18 Javascript
AngularJs Injecting Services Into Controllers详解
Sep 02 Javascript
JavaScript的for循环中嵌套一个点击事件的问题解决
Mar 03 Javascript
vue实现app页面切换动画效果实例
May 23 Javascript
通俗易懂地解释JS中的闭包
Oct 23 Javascript
webpack vue项目开发环境局域网访问方法
Mar 20 Javascript
微信小程序实现左滑动删除效果
Mar 30 Javascript
iview form清除校验状态的实现
Sep 19 Javascript
javascript实现前端成语点击验证
Jun 24 Javascript
vue的过滤器filter实例详解
Sep 17 #Javascript
一步一步的了解webpack4的splitChunk插件(小结)
Sep 17 #Javascript
React Router V4使用指南(精讲)
Sep 17 #Javascript
关于vue编译版本引入的问题的解决
Sep 17 #Javascript
理顺8个版本vue的区别(小结)
Sep 17 #Javascript
vue.js编译时给生成的文件增加版本号
Sep 17 #Javascript
详解关于Vue版本不匹配问题(Vue packages version mismatch)
Sep 17 #Javascript
You might like
PHP面向对象三大特点学习(充分理解抽象、封装、继承、多态)
2012/05/07 PHP
PHP完全二叉树定义与实现方法示例
2017/10/09 PHP
phpstorm 正则匹配删除空行、注释行(替换注释行为空行)
2018/01/21 PHP
php-fpm中max_children的配置
2019/03/15 PHP
关于Yii中模型场景的一些简单介绍
2019/09/22 PHP
redis+php实现微博(二)发布与关注功能详解
2019/09/23 PHP
利用Javascript判断操作系统的类型实现不同操作系统下的兼容性
2013/01/29 Javascript
关于Jquery操作Cookie取值错误的解决方法
2013/08/26 Javascript
JS数组array元素的添加和删除方法代码实例
2015/06/01 Javascript
JavaScript取得键盘按下方向键是哪个的方法
2015/08/04 Javascript
JS 拦截全局ajax请求实例解析
2016/11/29 Javascript
vue.js树形组件之删除双击增加分支实例代码
2017/02/28 Javascript
vue 点击按钮增加一行的方法
2018/09/07 Javascript
layui导出所有数据的例子
2019/09/10 Javascript
使用vscode快速建立vue模板过程详解
2019/10/10 Javascript
uni-app微信小程序登录授权的实现
2020/05/22 Javascript
[33:28]完美世界DOTA2联赛PWL S3 PXG vs GXR 第三场 12.19
2020/12/24 DOTA
Python解析网页源代码中的115网盘链接实例
2014/09/30 Python
Ubuntu 14.04+Django 1.7.1+Nginx+uwsgi部署教程
2014/11/18 Python
详解Python中列表和元祖的使用方法
2015/04/25 Python
Python自动化运维之Ansible定义主机与组规则操作详解
2019/06/13 Python
python3 打印输出字典中特定的某个key的方法示例
2019/07/06 Python
python3 线性回归验证方法
2019/07/09 Python
在 Python 中接管键盘中断信号的实现方法
2020/02/04 Python
matplotlib quiver箭图绘制案例
2020/04/17 Python
HTML5 Canvas的性能提高技巧经验分享
2013/07/02 HTML / CSS
天巡全球:Skyscanner Global
2017/06/20 全球购物
Myprotein亚太地区:欧洲第一在线运动营养品牌
2020/12/20 全球购物
为什么UNION ALL比UNION快
2016/03/17 面试题
linux面试题参考答案(8)
2016/04/19 面试题
道德演讲稿
2014/05/21 职场文书
党组织领导班子整改方案
2014/10/25 职场文书
职代会闭幕词
2015/01/28 职场文书
投标售后服务承诺书
2015/04/29 职场文书
《刷子李》教学反思
2016/02/20 职场文书
Vue3中toRef与toRefs的区别
2022/03/24 Vue.js