IE8 下访问webpack.UglifyJsPlugin 压缩的代码出错
SamHwang1990 opened this issue · 7 comments
环境
- 浏览器:IE8
- webpack: <=v1.14.0
- UglifyJS: >= 2.7.0
问题分析
使用IE8 加载webpack 打包压缩后的代码时,会出现类似'undefined' 为空或不是对象
的错误。
在进行简单的调试后,造成错误的原因是变量指向被改变了,表象是:压缩后的变量le
原本是指向jQuery
对象的,但在运行时却找不到jQuery
对象下的属性或方法导致出错。
由于压缩后的代码比较难理清,所以,也找不到到底是哪段代码造成了变量覆盖问题。
对UglifyJS 进行了一下研究,大概确定是mangle
阶段出了问题,貌似是做变量名混淆的。而在相关的选项中,有一个选项比较相关:--screw-ie8
。
查找了一下该选项的意义,看到有以下一段提交记录:
Regardless of the
--screw-ie
setting, the names will not be leaked.
Code relying on the IE bug will not work properly after mangling.
Without
--screw-ie
: a hack has been added to the mangler to avoid
using the same name for a function expression and some other variable in
the same scope. This keeps legit code working, at the (negligible,
indeed) cost of one more identifier.With
--screw-ie
you allow the mangler to name function expressions
with the same identifier as another variable in scope. After mangling
code might break in IE<9.
其中--screw-ie
后来改名为--screw-ie8
,大致上是对IE8 下带名字的函数表达式(Named Function Expression, NFE)做了一下hack。
IE8 的JS 引擎对NFE 有以下bug:JScript NFE bugs。简单理解就是,IE8 下NFE 会在作用域内声明并初始化一个变量,该变量名为函数表达式的名,值为该函数表达式。而这种行为在标准的Javascript 语法中是不存在的。
当UglifyJS 在mangle
阶段,若发现--screw-ie8
参数为false
,则尝试绕开这些bug,使函数表达式的名字不会与作用域中其他变量名冲突。
而在UglifyJS ~v2.7.0
后,参数--screw-ie8
就默认为true
了,即,如果不显式声明参数为false
,则mangle
的结果很可能造成NFE
变量覆盖了作用域内的同名变量。提交历史:Enable --screw-ie8 by default 。
解决方案
理论上,只要我们在调用UglifyJS 时,显式设置--screw-ie8
为false
即可避开压缩后的代码在IE8 下报错。如果使用UglifyJS API 调用的话,选项参数至少如下:
{
mangle: {
screw_ie8: false
}
}
基于webpack.optimize.UglifyJsPlugin 的解决方案
当开发者是结合webpack、webpack.optimize.UglifyJsPlugin 来做打包和压缩的话,情况就稍微复杂了。
在webpack@1.14.0
之前,{ mangle: { screw_ie8: false } }
都没有被正确传给UglifyJS,代码如下:
// webpack/lib/optimize/UglifyJsPlugin.js
if(options.mangle !== false) {
ast.figure_out_scope(); // 正确应该是将options 传到这个函数调用处
ast.compute_char_frequency(options.mangle || {});
ast.mangle_names(options.mangle || {});
if(options.mangle && options.mangle.props) {
uglify.mangle_properties(ast, options.mangle.props);
}
}
上面代码没有将screw_ie8
参数传到ast.figure_out_scope();
导致IE8 报错的问题没法绕开,除非屏蔽整个mangle
,但这样源码泄漏的风险就很高了。
前几天作者终于将该bug 提交到webpack@v1.x
了:Pass mangle options to ast.figure_out_scope in uglify。但新版还没发布,所以,暂时先将webpack 依赖版本指向该hash:
// package.json
"devDependencies": {
"webpack": "webpack/webpack#f66f024"
}
参考链接:
赞
这是天坑,我在开发模式下没有压缩代码。IE8正常,发布时压缩代码不正常。
或者使用 "webpack": "1.13.2"
因为 1.13.2 依赖的是 uglify-js@2.6.0
https://github.com/webpack/webpack/blob/v1.13.2/package.json#L19
webpack@1.14.0 未解决此问题
再补充个,因为 uglify2 karma@1.4.0 不支持IE8 ,需要使用 karma@1.3.0
karma-runner/karma#2556
在 webpack1 里面还有这个问题
webpack/webpack#1912