浅谈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 相关文章推荐
为Yahoo! UI Extensions Grid增加内置的可编辑器
Mar 10 Javascript
优化javascript的执行效率一些方法总结
Dec 25 Javascript
动态的创建一个元素createElement及删除一个元素
Jan 24 Javascript
IE6-IE9中tbody的innerHTML不能赋值的解决方法
Sep 26 Javascript
jQuery实现表单步骤流程导航代码分享
Aug 28 Javascript
分享两款带遮罩的jQuery弹出框
Dec 30 Javascript
原生JavaScript实现动态省市县三级联动下拉框菜单实例代码
Feb 03 Javascript
浅谈JS继承_借用构造函数 &amp; 组合式继承
Aug 16 Javascript
jQuery 选择器(61种)整理总结
Sep 26 Javascript
基于angularJS的表单验证指令介绍
Oct 21 Javascript
Vue实现调节窗口大小时触发事件动态调节更新组件尺寸的方法
Sep 15 Javascript
js 闭包深入理解与实例分析
Mar 19 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 命名空间实例说明
2011/01/27 PHP
PHP的AES加密算法完整实例
2016/07/20 PHP
Laravel如何使用Redis共享Session
2018/02/23 PHP
PHP面向对象五大原则之接口隔离原则(ISP)详解
2018/04/04 PHP
PHP判断函数是否被定义的方法
2019/06/21 PHP
javascript对talbe进行动态添加、删除、验证实现代码
2012/03/29 Javascript
Javascript 实现图片无缝滚动
2014/12/19 Javascript
JS实现文字向下滚动完整实例
2015/02/06 Javascript
jquery操作angularjs对象
2015/06/26 Javascript
用JavaScript来美化HTML的select标签的下拉列表效果
2015/11/17 Javascript
Bootstrap每天必学之响应式导航、轮播图
2016/04/25 Javascript
微信小程序前端源码逻辑和工作流
2016/09/25 Javascript
jQuery解析XML 详解及方法总结
2016/09/28 Javascript
Grunt针对静态文件的压缩,版本控制打包的实例讲解
2017/09/29 Javascript
详解使用React全家桶搭建一个后台管理系统
2017/11/04 Javascript
js合并两个数组生成合并后的key:value数组
2018/05/09 Javascript
js中的 || 与 &amp;&amp; 运算符详解
2018/05/24 Javascript
vue实现在v-html的html字符串中绑定事件
2019/10/28 Javascript
vue 清空input标签 中file的值操作
2020/07/21 Javascript
python基于右递归解决八皇后问题的方法
2015/05/25 Python
Python实现求最大公约数及判断素数的方法
2015/05/26 Python
Python selenium文件上传方法汇总
2020/11/19 Python
解决python文件字符串转列表时遇到空行的问题
2017/07/09 Python
利用TensorFlow训练简单的二分类神经网络模型的方法
2018/03/05 Python
使用python绘制3维正态分布图的方法
2018/12/29 Python
Python面向对象程序设计OOP入门教程【类,实例,继承,重载等】
2019/01/05 Python
python实现批量注册网站用户的示例
2019/02/22 Python
python如何基于redis实现ip代理池
2020/01/17 Python
使用CSS3制作倾斜导航条和毛玻璃效果
2017/09/12 HTML / CSS
css3 position fixed固定居中问题解决方案
2014/08/19 HTML / CSS
Html5游戏开发之乒乓Ping Pong游戏示例(三)
2013/01/21 HTML / CSS
HTML5 embed 标签使用方法介绍
2013/08/13 HTML / CSS
意大利自行车商店:Cingolani Bike Shop
2019/09/03 全球购物
相亲大会策划方案
2014/06/05 职场文书
2014年护理工作总结范文
2014/11/14 职场文书
python 统计代码耗时的几种方法分享
2021/04/02 Python