浅谈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 相关文章推荐
javascript据option的value值快速设定初始的selected选项
Aug 13 Javascript
Dom 是什么的详细说明
Oct 25 Javascript
触屏中的JavaScript事件分析
Feb 06 Javascript
Bootstrap精简教程
Nov 27 Javascript
jQuery简单实现上下,左右滑动的方法
Jun 01 Javascript
jQuery异步提交表单的两种方式
Sep 13 Javascript
基于JS实现仿京东搜索栏随滑动透明度渐变效果
Jul 10 Javascript
Vue修改mint-ui默认样式的方法
Feb 03 Javascript
基于Vuex无法观察到值变化的解决方法
Mar 01 Javascript
vue模仿网易云音乐的单页面应用
Apr 24 Javascript
vue实现简单跑马灯效果
May 25 Javascript
解决Vue的项目使用Element ui 走马灯无法实现的问题
Aug 03 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
js限制checkbox勾选的个数以及php获取多个checkbbox的方法深入解析
2013/07/18 PHP
PHP依赖倒置(Dependency Injection)代码实例
2014/10/11 PHP
PHP+JS实现大规模数据提交的方法
2015/07/02 PHP
PHP模拟asp.net的StringBuilder类实现方法
2015/08/08 PHP
PHP中Socket连接及读写数据超时问题分析
2016/07/19 PHP
jquery刷新页面的实现代码(局部及全页面刷新)
2011/07/11 Javascript
在Javascript中 声明时用&quot;var&quot;与不用&quot;var&quot;的区别
2013/04/15 Javascript
深入分析JSONP跨域的原理
2014/12/10 Javascript
浅谈JavaScript正则表达式分组匹配
2015/04/10 Javascript
在JavaScript的正则表达式中使用exec()方法
2015/06/16 Javascript
jQuery toggle 代替方法
2016/03/22 Javascript
学习AngularJs:Directive指令用法(完整版)
2016/04/26 Javascript
js事件冒泡、事件捕获和阻止默认事件详解
2016/08/04 Javascript
JavaScript 异步调用
2017/10/25 Javascript
Vue组件和Route的生命周期实例详解
2018/02/10 Javascript
浅析JS中回调函数及用法
2018/07/25 Javascript
Electron autoUpdater实现Windows安装包自动更新的方法
2018/12/24 Javascript
javascript canvas检测小球碰撞
2020/04/17 Javascript
vue + el-form 实现的多层循环表单验证
2020/11/25 Vue.js
[58:12]Ti4第二日主赛事败者组 LGD vs iG 3
2014/07/21 DOTA
Python用GET方法上传文件
2015/03/10 Python
Python访问纯真IP数据库脚本分享
2015/06/29 Python
Python解决抛小球问题 求小球下落经历的距离之和示例
2018/02/01 Python
Python单元测试unittest的具体使用示例
2018/12/17 Python
Python3实现定时任务的四种方式
2019/06/03 Python
解决pycharm下os.system执行命令返回有中文乱码的问题
2019/07/07 Python
浅析Django中关于session的使用
2019/12/30 Python
HTML5 Canvas鼠标与键盘事件demo示例
2013/07/04 HTML / CSS
汇集了世界上最好的天然和有机美容产品:LoveLula
2018/02/05 全球购物
建筑班组长岗位职责
2014/01/02 职场文书
捐书倡议书
2014/08/29 职场文书
公司领导九九重阳节发言稿2014
2014/09/25 职场文书
小学教师师德整改措施
2014/09/29 职场文书
离婚协议书范文2015
2015/01/26 职场文书
2015年光棍节活动总结
2015/03/24 职场文书
《现实主义勇者的王国再建记》第三弹OST全曲试听片段公开
2022/04/04 日漫