关于新版本 webpack gulp 兼容 IE8- 的正确姿势

最近在使用 webpack 和 gulp 的时候发现压缩后的 js 在 IE8- 下各种报错,蛋蛋的忧桑。
于是花了几个小时整理了下问题及解决方案。

PS: webpack 的 es3ify-loader 插件因为所在层面不同 (解析层之后才是优化层,如压缩等),所以问题依旧。

问题的核心点

不论是 webpack 还是 gulp 或者是其他第三方压缩插件,基本上都是基于 uglify-js 来压缩代码的,所以问题的根本原因就是 uglify-js 版本过高,导致不兼容 IE8-。

虽然问题描述起来非常简单,但怎么解决呢?

  1. 工具版本回退 (将 gulp, webpack 回退到老版本)
  2. 在 gulp/webpack 处理完后在使用老版本 uglify-js 压缩
  3. 修改工具源码 (这是找死行为!!!)
  4. 基于node的模块系统劫持 (感觉不错)
  5. 暂时没想到…

确定兼容版本

既然知道了问题的关键点,那自然是找兼容的版本了。
经过测试后最终确定 uglify-js@2.6.4 版本兼容 IE8-,至于如何测试,最简单的方法用如下代码进行压缩测试。

1
var obj = {class: 'class'};

压缩后如果是 var obj={class:"class"} 那就是不兼容的,如果是 var obj={"class":"class"} 那就是兼容的。

针对性解决疑难杂症

我想到的第一个方法自然是版本回退,因为这个方法最简单了,而且项目里确实也是这么用的。
比如 gulp-uglify 使用 1.5.4 即可兼容,webpack1 使用 1.12.15 版本即可,至于 webpack2,,相当麻烦。
以上是 gulp 和 webpack1 的版本回退方法,非常简单易用。

webpack2 稳定版本完全不兼容,我测试了各个版本确定了 2.1.0-beta.22 版本。
但这不是最终发布版本,肯定不会用这个,那怎么破?继续看下文了。

针对 webpack2 进行模块劫持

由于 node 模块是针对模块绝对路径进行缓存的,那就简单了,
我们直接写一个缓存进去,webpack 就会加载我们的缓存,这样就达到兼容目的。
这里就不解释模块加载原理,感兴趣的自己用 devtool 或者是 vscode 跟踪下 require 行为即可。

第一步,安装 webpack 和 uglify-js@2.6:

1
$ npm i -D webpack uglify-js@2.6

第二步,劫持webpack内的uglify-js模块:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var path = require('path');

/** 利用模块缓存劫持 webpack 内部的 uglify-js **/
const uglifyJs = require('uglify-js');
const moduleUglifyJs = module.children.filter(m => m.exports === uglifyJs);
const key = path.join(__dirname, 'node_modules/webpack/node_modules/uglify-js/tools/node.js');
module.constructor._cache[key] = moduleUglifyJs[0];
/** 劫持完成 **/

const webpack = require('webpack');

// 这里正常使用
module.exports = {
entry: './src/*.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].bundle.js',
},
plugins: [
new webpack.optimize.UglifyJsPlugin({
sourceMap: false,
}),
],
};

小节

其实我一开始是打算用方案二的,因为这是传统思路嘛,大多数人应该会想到这个。
但我这个人比较懒,能不改就不改,所以花了点时间折腾下,一劳永逸。

如果之后又出什么幺蛾子,,我可能会采取方案二,毕竟传统、稳定嘛。