webpack4:连奏中的进化
webpack4在2月底的时候发布,这次webpack4有了一个名字"Legato",也就是"连奏"的意思,寓意webpack在不断进化,而且是无缝(no-gaps)的进化。webpack的进化点是通过捐赠者和用户投票来决定的,之前在介绍webpack3的时候,曾给出过投票数在前几名的优化点,集中在用户体验、构建性能(速度和产出大小)、通用和适配性(es module、typescript、web assemble)等。webpack4发布了,下面将结合文档和实践,验证一下webpack是否兑现了当初的诺言。
webpack4中的新特性
webpack3官方发布的时候,提到了下个版本可能的改动点,翻译过来如下所示:
- 高性能的构建缓存
- 提升初始化速度和增量构建效率
- 更好的支持Type Script
- 修订长期缓存
- 支持WASM 模块支持
- 提升用户体验
webpack4官方发布的文档之中主要提及了以下新特性:
支持零配置(Zero Configuration)
该特性主要用于解决webpack的门槛高问题,webpack是一个配置声明式的操作模式,npm、gulp是指令式的,需要描述每一步是干什么的,而webpack的配置项凌乱且无序,让很多开发者头疼。
webpack4提供了零配置方案,默认入口属性为./src,默认输出路径为./dist,不再需要配置文件,实现了开箱即用的封装能力,更通俗的讲,webpack会自动查找项目中src目录下的index.js文件,然后选择的模式进行相应的打包操作,最后新建dist目录并生成一个main.js文件。此外针对开发环境和线上环境提供了两种打包模式:"production"和"development","production"模式内置了项目产出时的基本配置项,"development"模式基本满足了快速构建和开发体验。使用的方法是在运行webapck命令的时候,设置好mode参数的值即可,默认是production属性。
"scripts": {
"dev": "webpack --mode development",
"build": "webpack --mode production"
}
具体的案例可以前往github进行下载。
下面根据官方的文档介绍一下两种模式。
- Production模式
提供了发布程序时的优化配置项,旨在更小的产出文件、更快的运行速度、不暴露源码和路径。会默认采用代码压缩(minification),作用域提升(scope hoisting),tree-shaking,NoEmitOnErrorsPlugin,无副作用模块修剪(side-effect-free module pruning)等。
- Development模式
旨在提升开发调试过程中的体验,如更快的构建速度、调试时的代码易读性、暴露运行时的错误信息等。会默认采用bundle的输出包含路径名和eval-source-map等,提升代码的可读性和构建速度。
两种模式就是两个箱子,箱子里面就是各种插件工具,只是有些是开启的,有些是关闭的,具体有哪些工具可以参考这篇文章。
废弃CommonsChunkPlugin
webpack4废弃了CommonsChunkPlugin,引入了optimization.splitChunks和optimization.runtimeChunk,旨在优化chunk的拆分。先介绍一下code splitting下的CommonsChunkPlugin有什么缺点,然后仔介绍一下chunk split是怎么进行优化的。
- CommonsChunkPlugin的问题
CommmonsChunkPlugin的思路是Create this chunk and move all modules matching minChunks into the new chunk,即将满足minChunks配置想所设置的条件的模块移到一个新的chunk文件中去,这个思路是基于父子关系的,也就是这个新产出的new chunk是所有chunk的父亲,在加载孩子chunk的时候,父亲chunk是必须要提前加载的。举个例子:
example:
entryA: vue vuex someComponents
entryB: vue axios someComponents
entryC: vue vux axios someComponents
minchunks: 2
产出后的chunk:
vendor-chunk:vue vuex axios
chunkA~chunkC: only the components
带来的问题是:对entryA和entryB来说,vendor-chunk都包含了多余的module。
CommonsChunkPlugin另外一个问题是:对异步的模块不友好。下面也举例说明:
example:
entryA: vue vuex someComponents
asyncB:vue axios someComponents
entryC: vue vux axios someComponents
minchunks: 2
产出后的chunk:
vendor-chunk:vue vuex
chunkA: only the components
chunkB: vue axios someComponents
chunkC: axios someComponents
带来的问题是:如果asyncB在entryA中动态引入,则会引入多余的module。
总的来说CommonsChunkPlugin有以下三个问题:
- 产出的chunk在引入的时候,会包含重复的代码;
- 无法优化异步chunk;
- 高优的chunk产出需要的minchunks配置比较复杂。
- SplitChunksPlugin
与CommonsChunkPlugin的父子关系思路不同的是,SplitChunksPlugin引入了chunkGroup的概念,在入口chunk和异步chunk中发现被重复使用的模块,将重叠的模块以vendor-chunk的形式分离出来,也就是vendor-chunk可能有多个,不再受限于是所有chunk中都共同存在的模块,原理的示意如下图所示:

其中,可以发现SplitChunksPlugin产出的vendor-chunk有多个,对于入口A来说,引入的代码只有chunkA、vendor-chunkA-B、vendor-chunkA-C、vendor-chunkA-B-C;这时候chunkA、vendor-chunkA-B、vendor-chunkA-C、vendor-chunkA-B-C形成了一个chunkGroup。下面举个列子:
example:
entryA: vue vuex someComponents
entryB:vue axios someComponents
entryC: vue vux axios someComponents
产出后的chunk:
vendor-chunkA-C:vuex
vendor-chunkB-C:axios
vendor-chunkA-B-C:vue
chunkA: only the components
chunkB: only the components
chunkC: only the components
SplitChunksPlugin能够解决掉CommonsChunkPlugin中提到的三个问题,SplitChunksPlugin在production模式下是默认开启的,但是它默认只作用于异步chunk,如果要作用于入口chunk的话,需要配置optimization.splitChunks.chunks: "all",同时webpack自动split chunks是有几个限制条件的:
- 新产出的vendor-chunk是要被共享的,或者模块来自npm包;
- 新产出的vendor-chunk的大小得大于30kb;
- 并行请求vendor-chunk的数量不能超出5个;
- 对于entry-chunk而言,并行加载的vendor-chunk不能超出3个。
这些限制可以在SplitChunks的默认配置项中可以一一对应的看到。
splitChunks: {
chunks: "async",
minSize: 30000,
minChunks: 1,
maxAsyncRequests: 5,
maxInitialRequests: 3,
name: true,
cacheGroups: {
default: {
minChunks: 2,
priority: -20
reuseExistingChunk: true,
},
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10
}
}
}
其实不难理解这些限制,因为SplitChunksPlugin产生的结果就是原来chunk被拆分了,引入的文件数量会变多,因此需要在文件数量上进行限制。
- runtimeChunkPlugin
在使用CommonsChunkPlugin的时候,我们通常会把webpack runtime的基础函数提取出来,单独作为一个chunk,毕竟code splitting想把不变的代码单独抽离出来,方便浏览器缓存,提升加载速度。webpack4废弃了CommonsChunkPlugin,采用了runtimeChunkPlugin可以将每个entry chunk中的runtime部分的函数分离出来,只需要一个简单的配置:optimization.runtimeChunk: true。
sideEffects
在webapck2开始支持ESModule后,webpack提出了tree-shaking进行无用模块的消除,主要依赖ES Module的静态结构。在webapck4之前,主要通过在.babelrc文件中设置"modules": false来开启无用的模块检测,该方法显然比较粗暴。webapck4灵活扩展了如何对某模块开展无用代码检测,主要通过在package.json文件中设置sideEffects: false来告诉编译器该项目或模块是pure的,可以进行无用模块删除。
官方的github上提供了一个sideEffects的demo示例供参考,如果对tree-shaking的概念不是太了解,可去官方的文档中tree-shaking部分详细了解。下面是官方提供的一个减包效果示例,仅供欣赏。

支持压缩ES6+代码
在webapck4之前,webpack.prod.conf.js中关于UglifyJsPlugin的注释会有这么一段话:
// UglifyJs do not support ES6+, you can also use babel-minify for better treeshaking: https://github.com/babel/minify
意思就是UglifyJs无法对ES6+的代码进行压缩,需使用babel-minify获取更好的treeshaking效果。webapck4目前已经支持压缩ES6+的代码。
// 源代码
import {sayHello} from './libs/js/util.js'
let output = sayHello('hwm');
console.log(output);
// 产出
...let r=(e=>`Hello ${e}!`)("hwm");console.log(r)}...
支持更多的模块类型
webpack4支持以import和export形式加载和导出本地的WebAssembly模块,这一块本人实际项目并未使用到,暂不做介绍;此外,webpack4支持json模块和tree-shaking,之前json文件的加载需要json-loader的支持,webpack4已经能够支持json模块(JSON Module),不需要额外的配置;此外,当json文件用ESModule的语法import引入的时候,webpack4还能支持对json模块进行tree-shaking处理,把用不到的字段过滤掉,起到减包的作用。下面是个示例:
// 引用数据的三种方法
let jsonData = require('./data/test.json');
import jsonData from './data/test.json'
// 打包时只会提取test.json文件中onePart部分。
import { onePart } from './data/test.json'
如何迁移升级到webpack4
0配置的局限性
webpack4声称能够0配置,但是0配置有很多局限性,比如只能是单入口的项目,入口和产出的文件名是固定的,entry是src目录下的index.js,产出是dist目录下的main.js,很明显不能满足实际项目应用。于是,开发者还是得自己配置webpack.config.js文件。
webpack4配置文件的变化点
如何配置webpack4下的配置文件,需要大致了解webapck4的配置项的改动点。
mode:开发模式 development
- 开启dev-tool,方便浏览器调试
- 提供详细的错误提示
- 利用缓存机制,实现快速构建
- 开启output.pathinfo,在产出的bundle中显示模块路径信息
- 开启NamedModulesPlugin
- 开启NoEmitOnErrorsPlugin
mode:生产模式 production
- 启动各种优化插件(ModuleConcatenationPlugin、optimization.minimize、ModuleConcatenationPlugin、Tree-shaking),压缩、合并、拆分,产出更小体积的chunk
- 关闭内存缓存
- 开启NoEmitOnErrorsPlugin
plugin
- 内置optimization.minimize来压缩代码,不用再显示引入UglifyJsPlugin;
- 废弃CommonsChunkPlugin插件,使用optimization.splitChunks和optimization.runtimeChunk来代替;
- 使用optimization.noEmitOnErrors来替换NoEmitOnErrorsPlugin插件
- 使用optimization.namedModules来替换NamedModulesPlugin插件
loader
- 废弃json-loader,友好支持json模块,以ESMoudle的语法引入,还可以对json模块进行tree-shaking处理;
其他的改动信息建议查看webpack的中文升级日志。
vue-cli项目如何改造
介绍完了webpack4中核心配置项的变化,接下来结合vue-cli示例项目介绍一下,如何配置webpack.conf.js文件。
1.升级npm包版本
建议升级webpack4,同时升级涉及到的loaders和plugins。webpack4 中 cli 工具分离成了 webpack 核心库 与 webpack-cli 命令行工具两个模块,需要使用 CLI ,必安装 webpack-cli 至项目中。
npm i webpack webpack-cli webpack-dev-server -D
涉及到的插件有:
"vue-loader": "^15.0.10",
"vue-style-loader": "^4.1.0",
"vue-template-compiler": "^2.5.16",
"copy-webpack-plugin": "^4.0.1",
"extract-text-webpack-plugin": "^4.0.0-beta.0",
"html-webpack-plugin": "^3.1.0",
"optimize-css-assets-webpack-plugin": "^4.0.0",
"webpack-bundle-analyzer": "^2.11.1",
"webpack-dev-middleware": "^3.1.2",
"webpack-dev-server": "^3.1.3",
"webpack-merge": "^4.1.0"
2.修改webpack.base.conf.js
webpack4推荐使用了最新版本的vue-loader("vue-loader": "^15.0.10"),但是最新的vue-loader需要在webapck config文件中设置VueLoaderPlugin插件,否则会报以下错误:
vue-loader was used without the corresponding plugin. Make sure to include VueLoaderPlugin in your webpack config.
webpack.base.conf.js文件中的改动主要是添加VueLoaderPlugin插件。
const { VueLoaderPlugin } = require('vue-loader');
module.exports = {
...
plugins: [
// 添加VueLoaderPlugin,以响应vue-loader
new VueLoaderPlugin()
],
...
}
3.修改webpack.dev.conf.js
添加mode属性,并设置为development模式;然后注释掉 webpack4开发模式已经内置的插件,如webpack.NamedModulesPlugin和webpack.NoEmitOnErrorsPlugin 插件。

4.修改webpack.prod.conf.js
添加mode属性,并设置为production模式;然后注释掉 webpack4生产模式已经内置的插件,如CommonsChunkPlugin、uglifyjs-webpack-plugin 、ModuleConcatenationPlugin插件;最后根据webpack4提供的文档配置optimization,使用splitChunks和runtimeChunk完成chunk的提取和优化。

const webpackConfig = merge(baseWebpackConfig, {
...
optimization: {
// 采用splitChunks提取出entry chunk的chunk Group
splitChunks: {
cacheGroups: {
// 处理入口chunk
vendors: {
test: /[\\/]node_modules[\\/]/,
chunks: 'initial',
name: 'vendors',
},
// 处理异步chunk
'async-vendors': {
test: /[\\/]node_modules[\\/]/,
minChunks: 2,
chunks: 'async',
name: 'async-vendors'
}
}
},
// 为每个入口提取出webpack runtime模块
runtimeChunk: { name: 'manifest' }
}
...
})
经过以上操作,我们已经基本完成了vue-cli项目的改造。运行项目的时候,注意看控制台的报错,是哪个插件报的错就去升级那个插件,如果存在找不到模块之类的错误就去升级对应的loader。vue-cli项目webapck4下配置demo已经上传到github,欢迎下载。
实例说话—webpack4的性能如何
webapck4旨在开发模式下提升构建速度、提升用户体验,在生产模式下减小产出包的大小,提升加载和运行速度,下面是webapck3和webapck4下vue-cli的webapck模板项目的实际效果截图:
开发者模式下:


由上图可以知道:webapck4下的构建速度是3703ms,明显由于webapck3下的5617ms;
生产模式下:


由上图可以知道:webapck4下的app-chunk的大小是933byte,明显小于webapck3下的11.6K;webapck4下vendor-chunk的大小是109K,小于webapck3下的112K。
两种模式下,webapck4性能上的确是精进不少,虽然有各种坑,还是建议把坑填了,升级到webpack4。
webpack的未来
想了解webpack的未来,建议先过一下webpack的历史。
webpack1支持CMD和AMD,同时拥有丰富的plugin和loader,webpack逐渐得到广泛应用。
webpack2相对于webpack最大的改进就是支持ES Module,可以直接分析ES Module之间的依赖关系,而webpack1必须将ES Module转换成CommonJS模块之后,才能使用webpack进行下一步处理。除此之外webpack2支持tree sharking,与ES Module的设计思路高度契合。
webpack3相对于webpack2,过渡相对平稳,但是新的特性大都围绕ES Module提出,如Scope Hoisting和Magic Comment。
webpack4集中发力在用户体验、构建性能(速度和产出大小)、通用和适配性(es module、typescript、web assemble)。
展望未来,webpack的未来肯定持续提升用户体验、降低使用门槛,进一步提升构建速度和产出代码的性能,同时webpack的团队已经承诺会根据社区的投票来决定新特性开发优先权。以下是公告中给出的未来的重点关注点:
- 继续修订长期缓存
- webapck任务多线程化,提升初始化速度和增量构建效率
- 提升CSS到一等公民,引入CSS Module Type ,废弃ExtractTextWebpackPlugin
- 提升HTML到一等公民,引入HTML Module Type
- 继续扩展0CJS(0配置文件),加入更多扩展
- 优化支持WASM 模块,从实验阶段过渡到稳定阶段
- 持续提升用户体验
参考文献
webapck4官方medium pr稿
webpack4中文升级日志
webpack4升级指南以及从webpack3.x迁移
Webpack4 新特性 及 Vue-cli项目升级
Webpack4官方指导教程
webpack4.0打包优化策略整理
webapck3新特性
webpack4:连奏中的进化的更多相关文章
- webpack4.27.1中遇到的错误
1:ERROR in Entry module not found: Error: Can't resolve './src' 我在使用webpack命令时报错,这时因为我的配置文件有问题webpac ...
- webpack4.0各个击破(1)—— html部分
webpack作为前端最火的构建工具,是前端自动化工具链最重要的部分,使用门槛较高.本系列是笔者自己的学习记录,比较基础,希望通过问题 + 解决方式的模式,以前端构建中遇到的具体需求为出发点,学习we ...
- webpack4.0各个击破(4)—— Javascript & splitChunk
目录 一. Js模块化开发 二. Js文件的一般打包需求 三. 使用webpack处理js文件 3.1 使用babel转换ES6+语法 3.2 脚本合并 3.3 公共模块识别 3.4 代码分割 3.5 ...
- babel7中 corejs 和 corejs2 的区别
babel7中 corejs 和 corejs2 的区别 最近在给项目升级 webpack4 和 babel7,有一些改变但是变化不大.具体过程可以参考这篇文章 webpack4:连奏中的进化.只是文 ...
- Webpack4.0各个击破(1)html篇
webpack作为前端最火的构建工具,是前端自动化工具链最重要的部分,使用门槛较高.本系列是笔者自己的学习记录,比较基础,希望通过问题 + 解决方式的模式,以前端构建中遇到的具体需求为出发点,学习we ...
- 浅谈webpack4.0 性能优化(转)
前言:在现实项目中,我们可能很少需要从头开始去配置一个webpack 项目,特别是webpack4.0发布以后,零配置启动一个项目成为一种标配.正因为零配置的webpack对项目本身提供的“打包”和“ ...
- webpack4试水总结
看了官方的升级通告,据说webpack4的打包效率提升近一倍,于是最近在项目分支上升级了下webpack4,过程中的一些报错及问题简单整理下,以供交流. 在之前的旧项目上单纯的升级webpack版本后 ...
- webpack4.x最详细入门讲解
前言 本文主要从webpack4.x入手,会对平时常用的Webpack配置一一讲解,各个功能点都有对应的详细例子,所以本文也比较长,但如果你能动手跟着本文中的例子完整写一次,相信你会觉得Webpack ...
- 【webpack系列】webpack4.x入门配置基础(一)
一.前言 webpack在不断的迭代优化,目前已经到了4.29.6.在webpack4这个版本中,做了很多优化,引入了很多特性,将获得更多模块类型,.mjs支持,更好的默认值,更为简洁的模式设置,更加 ...
随机推荐
- Java-transient总结
纸上得来终觉浅,绝知此事要躬行 --陆游 问渠那得清如许,为有源头活水来 --朱熹 transient有"临时的","短暂的"含义,我们了解过Seri ...
- 【Java编程】Eclipse快捷键
Alt+左箭头,右箭头 以在编辑窗口切换标签 Alt+上下箭头, 以自动选择鼠标所在行,并将其上下移动 Ctrl+f6 ...
- Android帧布局(Frame Layout)
Android帧布局(Frame Layout) FrameLayout是最简单的一个布局管理器.FrameLayout为每个加入其中的组件创建一个空白区域(一帧),这些组件根据layout_grav ...
- Zip操作的工具类
/** * Copyright 2002-2010 the original author is huanghe. */package com.ucap.web.cm.webapp.util; ...
- 【Qt编程】基于Qt的词典开发系列<十一>系统托盘的显示
本文主要讨论Qt中的系统托盘的设置.系统托盘想必大家都不陌生,最常用的就是QQ.系统托盘以简单.小巧的形式能让人们较快的打开软件.废话不多说,下面开始具体介绍. 首先,新建一个Qt Gui项目,类型选 ...
- Android特效专辑(三)——自定义不一样的Toast
Android特效专辑(三)--自定义不一样的Toast 大家都知道,Android的控件有时候很难满足我们的需求,所以我们需要自定义View.自定义的方式很多,有继承原生控件也有直接自定义View的 ...
- android 获取Bitmap位图所占用的内存大小
今天在看Universal-Image-Loader源码的时候,在对图片的超过用户在所设的阈值的时候,系统会调用GC将LinkHashMap比较靠底层的图片引用去掉,这里涉及到一个技术单个图片的文图大 ...
- java--交通灯管理系统
转载请申明出处:http://blog.csdn.net/xmxkf/article/details/9944947 .交通灯管理系统的业务和需求分析 交通灯管理系统的项目需求: 模拟实现十字路口的交 ...
- 和菜鸟一起学linux总线驱动之i2c死锁问题
不知不觉中已经有好几个月没有写点东西了,懒了就是懒了,说是忙着想把产品做得更好,都是借口,每天花一点时间来写点东西确实很不错,自己也坚持了很久很久,只不过今年以来,发现提高不是很大,能写的东西好少好少 ...
- Linux下安装MQ
1.下载Linux下MQ的安装包,网上下载试用版或购买正版,此处以7.0.0.0版为例安装 2.如上图所示,是linux的MQ安装包展开图 3.创建用户和用户组 >root用户连接linux & ...