blog icon indicating copy to clipboard operation
blog copied to clipboard

babel6默认添加use strict引发的问题和解决方案

Open shanggqm opened this issue 9 years ago • 12 comments
trafficstars

问题描述

我们有一个遗留系统,其中所有模块全部按照非strict模式,使用ES5语法书写。现在系统中新增一个全新的模块,我们希望能在这个新模块中引入ES2015语法来书写代码,因此引入了babel编译器来实现ES2015-> ES5的翻译,babel的版本是6.*。

在一次测试的时候我们发现Firefox的控制台报了这样一个错误:

TypeError: access to strict mode caller function is censored

字面意思就是在严格模式(strict mode)下使用了caller,这个不符合strict mode的要求,所以报错。

实际上,我们在书写ES2015模块时的确显式地在模块开始的第一行声明了:

'use strict';

这些ES2015的模块里引用了一个遗留模块Ajax模块,这个模块里使用了caller这种写法,因此报错。

问题定位

本以为是新增的ES2015模块声明的use strict的问题,直接删掉就可以解决问题,结果发现删掉后问题依旧,因此怀疑是否是Babel在转换时动了手脚。

因为项目中使用了Webpack来做构建,引入了babel-loader来做转换,所以通过看构建后的源码发现,虽然刚才手动删掉了自己写的'use strict',但是构建时又被自动的加上了。因此基本可以定位是babel搞的鬼。

google查了一下 关键字: babel use strict,找到这样一个页面:How to remove global “use strict” added by babel

其中给出了问题和解决方案: 问题的原因是babel的es2015预设中默认使用了babel-plugin-transform-strict-mode,为所有模块自动加上**'use strict'**, 所以才有了上面的问题。

解决方案当然也呼之欲出: 对于Babel5有一种特殊的解决方案:

//在.babelrc文件中加入这样一行配置:
blacklist: ["useStrict"]

但是在Babel6中这个方案不生效,构建时会报错:

Module build failed: ReferenceError: 
[BABEL] E:\workspaces\branches\bundler\myapp\src\main\webapp\app.es6.js: Using removed Babel 5 option: 
E:\workspaces\branches\bundler\myapp\src\main\webapp\.babelrc.blacklist - 
Put the specific transforms you want in the `plugins` option

问题很明显,Babel6已经不支持blacklist这个option了,所以问题的答主给了一个babel6的解决方案:

因为babel给所有模块自动加'use strict'是通过babel-plugin-transform-strict-mode这个插件加的,因此在配置babel的时候不要使用es2015 的preset,自己手动指定plugins列表,把这个plugin排除在外不引进来就可以了。

查阅ES2015的preset发现压根没有这个plugins,在工程里的node_modules下搜索了一下,发现这个plugin是从transform-es2015-modules-commonjs这个插件里引入的。

至此终于定位到问题的根源了。

解决方案

由于无法限制ES2015模块依赖ES5的模块,所以理论上来说,只要是在ES2015模块里加了use strict,都会存在潜在的风险。所以最好的方式是去掉这个插件,或者再用一个插件移除掉所有的use strict,这样保证所有代码都在非严格模式下执行即可。

第一个方案显然是不靠谱了,除非不写import,否则必须要引入transform-es2015-modules-commonjs插件来做ES2015 -> commonJS Module 的转换,而引入了这个插件,就必须要引入babel-plugin-transform-strict-mode插件,这个很难在配置层面做到限制。

所以只能转向第二个方案:再写一个babel插件把所有的use strict干掉就可以了。习惯性地上github上搜索了一下,居然还真搜到了一个包:babel-plugin-transform-remove-strict-mode。果真是万能的Github!!!

粗略的看了下源码:

exports["default"] = function () {
    return {
        visitor: {
            Program: {
                exit: function exit(path) {
                    var list = path.node.directives;
                    for(var i=list.length-1, it; i>=0 ; i--){
                        it = list[i];
                        if (it.value.value==='use strict'){
                            list.splice(i,1);
                        }
                    }
                }

            }
        }
    };
};

module.exports = exports["default"];

再对比了一下babel-plugin-transform-strict-mode的源码:

import * as t from "babel-types";

export default function () {
    return {
        visitor: {
          Program(path, state) {
            if (state.opts.strict === false) return;

            let { node } = path;

            for (let directive of (node.directives: Array<Object>)) {
              if (directive.value.value === "use strict") return;
            }

            path.unshiftContainer("directives", t.directive(t.directiveLiteral("use strict")));
          }
        }
    };
}

源码我就不解读了,自己看吧。

确认无误后,在package.json文件中的devDependencies中加入

 "babel-plugin-transform-remove-strict-mode":"0.0.2"

然后在.babelrc文件中新增配置:

{ 
  "presets": [
    "es2015"
  ] ,
  "plugins":[
    "transform-runtime",
    //下面这行
    "transform-remove-strict-mode"
  ]
}

就把问题搞定了。

其他尚未确认的问题

  • 这个问题仅出现在firefox中,chrome中并未出现,至于是何原因,仍未深究,有兴趣的同学可以继续深挖一下。

shanggqm avatar Mar 30 '16 08:03 shanggqm

其实可以干掉 transform-es2015-modules-commonjs,用webpack2 自己的,可以支持ES6。 我是引用一些库,他是用caller,在use strict下面不能用,哎呀,他的代码还是混淆过的。 不知道为啥它要用这个逻辑。你确实也提供这个方法不错,不过最好的方法应该是从源头解决问题吧。

monkindey avatar Jun 04 '16 09:06 monkindey

I love you man, you saved my life !!!

kenberkeley avatar Aug 29 '16 07:08 kenberkeley

学习了。

showonne avatar Oct 12 '16 06:10 showonne

用 script-loader吧。。。

xialvjun avatar Nov 08 '16 15:11 xialvjun

I love you man, you saved my life !!!

ShaojieLiu avatar Jan 24 '18 05:01 ShaojieLiu

I love you man, you saved my life !!!

PasserbyFluid avatar May 18 '18 01:05 PasserbyFluid

I love you man, you saved my life !!!

VeigarH5 avatar Jul 31 '18 03:07 VeigarH5

I love you man, you saved my life !!!

carior avatar Jan 11 '19 03:01 carior

我按照你的方法,添加了插件,但是还是报错,错误相同,是不是此方法已经不适用于 babel 7了呢?

Hei-Ha avatar Apr 12 '19 06:04 Hei-Ha

是的呢,最新的babel已经不能这么搞了

SamBian86 avatar Jul 04 '19 09:07 SamBian86

那怎么办

cleverboy32 avatar Aug 06 '19 07:08 cleverboy32

add one line in .babelrc

{
  "sourceType": "script"
}

luckymore avatar Dec 06 '19 06:12 luckymore