升级到 webpack2

提醒:本文最后更新于 2847 天前,文中所描述的信息可能已发生改变,请谨慎使用。

前些天 webpack2.2 发布了正式版。个人觉得 webpack2 带来的最大的变化莫过于新的官网了 https://webpack.js.org,新官网带来的新的文档比之前的要好太多了。而且早就想试试 Tree Shaking 了,所以趁此时间赶紧升级了一把。虽然已经发布了 2.2 的正式版,不过目前还需要通过 npm install webpack@beta 来安装,大家要注意哦~最新消息,官方已经把 beta 去掉了,现在直接安装 npm install webpack 就是最新 2.2.1 的版本了,如果要装老版本的话需要 npm install -g webpack@1。另外官方给出了详细的webpack1升级到webpack2的迁移指南,也可以参考其对应的的中文翻译版。以下就按照我的认识给大家梳理一下 webpack2 的一些变化,详细的更新情况还请参考官方文档。

配置文件

新版 webpack.config.js 内的几个配置项发生了比较大的变化,如果你升级了之后什么都不修改的话默认会报错。

webpack 2 config error

可以看到配置项的确有些不同,其中变化最大的是loader的配置:

  1. 取消了 modules.preLoaders, modules.loaders, modules.afterLoaders,统一变成了 modules.rules
  2. 多个 loader 从之前的字符串链式拼接变成了 loader.use 数组形式。
  3. 所有的 loader 都默认必须补全 -loader,不能使用简写形式。据作者说是因为有些新手会把简写名称误以为是 loader 名称而和其它模块搞混淆了所以默认移除了这个功能。
  4. loader 的参数配置只能写在 rule.options 内部不能写在外面,例如 sass-loader 的参数配置方式是在 webpack.config.js 最外部增加的就不符合新版要求,使用的时候 webpack 会提示不合法参数的错误。

另外就是 module.resolve 的变化,resolve.root, resolve.moduleDirectoriesresolve.fallback 全部统一成了 resolve.modules,感觉更好理解了,讲道理之前完全记不住这几个东西。。。另外 resolve.extensions 不需要自己写一个空字符串在那了,webpack 会自动解析无后缀的模块加载。

config变化

编译大小的速度

更新好配置文件后就可以愉快的运行了。和之前的对比了一下,总体来说,编译速度的确是有优化,但优化幅度并不大。编译体积上虽然有 Tree Shaking 加成,但是对于这种大项目来说优化幅度并不是非常明显。总体上来说性能的话你最好别对新版本抱有太大的期望,以下是 Firekylin 两个版本编译前后的对比:

webpack1_vs_2

webpackv1(ms)v2(ms)
开发环境1541611502
生产环境3584421279

新特性

撇开 webpack 尴尬的编译速度不说,我们来看看 v2 版本为我们带来了哪些新特性吧。

Promise 化

可以看到新版很多代码都 Promise 化了,首先是配置文件 Promise 化了。之前 webpack.config.js 返回的永远是一个配置对象,现在能支持返回一个函数,函数参数是命令行传入的一些自定义环境参数,函数执行返回一个 Promise 对象。变成函数后我们就可以在配置文件内根据环境参数自定义配置了,不用像以前一样通过不同的文件区分不同的环境。返回 Promise 的话有助于我们在配置文件中执行一些异步操作并返回结果。不过感觉这个需求比较小众,目前我都还没有需求需要在配置文件中执行异步。

另外一个 Promise 化的东西就是分块代码加载 Promise 化了,不管是 require.ensure 还是 AMD 的 require 甚至是 ES6 的动态 import,返回的都是 Promise 的对象。所以对于一些不支持 Promise 的浏览器需要加载 Promise 的 Polyfill 库才行。ES6 的动态 import 在下面详细说说。

原生的 import 支持

旧版本只支持 AMD/CMD 和 CommonJS 等 require 加载模块,如果使用 ES6 语法的话需要使用 babel-loader 将其编译成 require 的版本才行。但是 import 加载最大的特点是它是编译时加载,在编译的时候我们就可以知道我们需要使用这个模块的哪些属性和方法,如果只是单纯的转换成 require 加载的话这些特性就都没了。好在 v2 版本已经原生支持了,Tree Shaking 通过静态分析只加载会被执行的代码也顺理成章的支持起来了。关于 Tree Shaking 更多的内容可以参考这篇文章:《webpack2 的 tree shaking 真的好用吗?》。TL;DR;总的观点基本上和我上面测试的结果一直,Tree Shaking 对代码函数式要求很高,对目前的代码来说优化程度寥寥。

原生 import 支持带来的另一个影响就是增加了新的异步分块代码加载方式。在老的版本中如果你想要分块加载一个模块基本写法如下:

require.ensure(['a'], function(require) {
  var a = require('a');
  a.dosomething();
}, 'chunkNameIfYouWant');

写法和 AMD require 类似采用回调后置了代码执行时间。webpack2 中原生支持的import函数加载模块,并返回 Promise 对象:

import('a').then(function(a) { 
  a.dosomething();
});

//async/await 版
(async function() {
  let a = await import('a');
  a.dosomething();
})();

Promise 化后带来的好处就是更加直观且语义化,配合 async/await 语法书写后基本就可以等同于同步代码理解。而且 Promise 带有 resolve 和 reject 两种方法,我们可以非常方便的 catch 到模块加载失败的错误并作出对应操作。不过 require.ensure 是支持第三个参数自定义分块代码的分块名称的,目前 import() 加载还不支持这个功能,如果需要自定义分块名称的话还是只能使用 require.ensure 的方式。

另外就是默认 babel-loader 还是会处理 import 方法的,为了不让其转换 import 方法从而让 webpack2 能够读取到 import,我们需要设置 babel-loader

{
  test: /\.js$/,
  loader: 'babel-loader',
  options: {
    presets: [ ['es2015', {module: false}] ]
  }
}

后记

总的来看 webpack2 带来的更新都是大势,ES6 和 Promise 的引入让代码更贴近标准,配置项归类的更加合理,特别是文档比以前的真的是好用太多了。而且 webpack 的作者最近都有加大力度在中文社区的推广,https://webpack-china.org webpack 中文社区也在扩张中,推荐大家多多升级交流。

Avatar
怡红公子 擅长前端和 Node.js 服务端方向。热爱开源时常在 Github 上活跃,也是博客爱好者,喜欢将所学内容总结成文章分享给他人。

6 评论

2018-02-16 12:55:59 回复

[…]升级到 webpack2 | 公子 | 2017-01-26[…]

论文服务 Chrome61.0 Windows 10
2017-11-08 14:24:34 回复

这个功能真好。

面试技巧 LBBROWSER Windows 7
2017-06-29 04:53:32 回复

功能不错

themebetter Sogou Explorer1.0 Windows 8.1
2017-03-14 08:37:12 回复

新特性还不错。

羽中 Firefox51.0 Windows 10
2017-02-15 01:28:03 回复

jwplayer用这个处理了js才注意到,不过压缩率很一般,好像主要用途不是这个?= =

公子 Chrome56.0 Mac OS 10.12.4
2017-02-15 13:44:47 回复

@羽中 哈哈,是一个 JS/HTML/CSS 资源打包用的,也能用来做压缩。