Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IE8 下访问webpack.UglifyJsPlugin 压缩的代码出错 #6

Open
SamHwang1990 opened this issue Feb 7, 2017 · 7 comments
Open

IE8 下访问webpack.UglifyJsPlugin 压缩的代码出错 #6

SamHwang1990 opened this issue Feb 7, 2017 · 7 comments

Comments

@SamHwang1990
Copy link
Owner

环境

  • 浏览器: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-ie8false即可避开压缩后的代码在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"
}

参考链接:

@xlitter
Copy link

xlitter commented Feb 8, 2017

@nimoc
Copy link

nimoc commented Feb 10, 2017

这是天坑,我在开发模式下没有压缩代码。IE8正常,发布时压缩代码不正常。

@nimoc
Copy link

nimoc commented Feb 13, 2017

或者使用 "webpack": "1.13.2"
因为 1.13.2 依赖的是 uglify-js@2.6.0
https://github.com/webpack/webpack/blob/v1.13.2/package.json#L19

@xlitter
Copy link

xlitter commented Feb 24, 2017

webpack@1.14.0 未解决此问题

@nimoc
Copy link

nimoc commented Feb 24, 2017

再补充个,因为 uglify2 karma@1.4.0 不支持IE8 ,需要使用 karma@1.3.0
karma-runner/karma#2556

https://github.com/fast-flow/support-ie8

@copilot-is
Copy link

在 webpack1 里面还有这个问题
webpack/webpack#1912

@lemures-t
Copy link

lemures-t commented Apr 25, 2017

真的是天坑。多谢楼主。
在 tags 里面找到了 1.15.0 这个版本已经修复了这个问题。应该也是 webpack1 的最后一个大版本了。
另外,1.15.0 依赖的是 uglifyJS 2.7.5

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants