1.前言

Babel 的 loose 模式将 ES6 代码转译成 ES5 代码,loose 模式是不太忠实于 ES6 语义的。这篇文章解释了它是怎么工作的以及它的优点与缺点(剧透:通常是不推荐的)。

在这之前,我们先简单了解一下 Babel 中的一些基础知识:

  • 配置文件

    Babel 的配置文件是.babelrc,存放在下项目的根目录下,该文件用来设置预设和插件,基本格式如下:

    {
      "presets":[],
      "plugins":[]
    }
    
  • presets(预设)

    为了将 Babel 的输出配置为符合要求的情况,我们需要指定 Babel 使用什么插件。可以指定的有:

    • 单独的插件
    • 预设,即支持各种编译方案的插件集合。

    下面是一些使用广泛的预设:

    • es2015:将 ES6 代码编译为 ES5
    • stage-3:将stage 3 ECMAScript proposals编译为ES5
    • react:将 JSX 编译为 JavaScript 并且移除 Flow 的类型注解

    预设通过 npm 安装。他们在 npm 中的包名称为其名字加上前缀babel-preset-。比如安装es2015时,我们可以用以下的命令:

    npm install babel-preset-es2015
    
  • plugins(插件)

    笼统地讲,插件是在编译过程中应用到输入中的函数。插件有两种重要的类别:

    • 语法插件。其作用为使 Babel 具备处理内建基础语法之外的语法实体。它们能够帮助构造抽象语法树(AST)。典型的语法插件有:syntax-async-functions 以及 syntax-jsx

    • 转换插件。其作用为修改抽象语法树。典型的转换插件有:transform-async-to-generatortransform-react-jsxtransform-es2015-arrow-functions 等。

      如果你想编译不包含在基础语法之内的代码,那你将同时需要一个语法插件以及与之相对应的转换插件。不过,每个依赖于语法插件的转换插件都将自动触发该语法插件。

      插件同样通过 npm 安装,他们在 npm 中的包名称为其名字加上前缀babel-plugin-

      • 安装插件syntax-jsxnpm install babel-plugin-syntax-jsx
      • 安装插件transform-react-jsxnpm install babel-plugin-transform-react-jsx

OK,基础知识介绍到这里,如想进一步学习 Babel,可以去到Babel官网。下面回归主题,探究 Babel 6 的 loose 模式。

2. 两种模式

许多 Babel 的插件有两种模式:

• 尽可能符合 ECMAScript6 语义的 normal 模式。

• 提供更简单 ES5 代码的 loose 模式。

通常,推荐不使用 loose 模式,使用这种模式的优点和缺点是:

• 优点:生成的代码可能更快,对老的引擎有更好的兼容性,代码通常更简洁,更加的“ES5化”。

• 缺点:你是在冒险——随后从转译的 ES6 到原生的 ES6 时你会遇到问题。这个险是很不值得冒的。

2.1 切换到 loose 模式

es2015-loose 是标准的 ES6 预设(preset)es2015 的 loose 版。这个预设的代码对什么样的插件有 loose 模式以及怎样开启提供了很好的说明。以下是代码截选:

module.exports = {
    plugins: [
      ···
      [require("babel-plugin-transform-es2015-classes"), {loose: true}],
      require("babel-plugin-transform-es2015-object-super"),
      ···
    ]
};

这是一个 CommonJS 模块,可以使用任何的 ECMAScript 5,如果你通过.babelrc 或者package.json配置 babel(详细配置),你需要使用 JSON。也可以包含 preset:

···
  "presets": [
    ···
    "es2015-loose",
    ···
  ],
 ···

也可以单独引入插件:

···
 "plugins": [
   ···
   ["transform-es2015-classes", {loose: true}],
   "transform-es2015-object-super",
   ···
 ],
 ···

3. 例子:normal 模式与 loose 模式的输出

让我们看看模式如何影响下面代码的编译:

class Point {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }
    toString() {
        return `(${this.x}, ${this.y})`;
    }
}

3.1 normal 模式

在 normal 模式下,类的 prototype 方法是通过Object.defineProperty添加的(第 A 行),来确保它们是不可以被枚举的,这是 ES6 规范所要求的。

"use strict";

    var _createClass = (function () {
        function defineProperties(target, props) {
            for (var i = 0; i < props.length; i++) {
                var descriptor = props[i];
                descriptor.enumerable = descriptor.enumerable || false;
                descriptor.configurable = true;
                if ("value" in descriptor) descriptor.writable = true;
                Object.defineProperty(target, descriptor.key, descriptor);   // (A)
            }
        }
        return function (Constructor, protoProps, staticProps) {
            if (protoProps) defineProperties(Constructor.prototype, protoProps);
            if (staticProps) defineProperties(Constructor, staticProps);
            return Constructor;
        };
    })();

    function _classCallCheck(instance, Constructor) {
        if (!(instance instanceof Constructor)) {
            throw new TypeError("Cannot call a class as a function");
        }
    }

    var Point = (function () {
        function Point(x, y) {
            _classCallCheck(this, Point);

            this.x = x;
            this.y = y;
        }

        _createClass(Point, [{
            key: "toString",
            value: function toString() {
                return "(" + this.x + ", " + this.y + ")";
            }
        }]);

        return Point;
    })();

3.2 loose 模式

在 loose 模式下,用通常的赋值方式添加方法(第 B 行),这种风格更像你用 ES5 手动编写代码。

     "use strict";

    function _classCallCheck(instance, Constructor) { ··· }

    var Point = (function () {
        function Point(x, y) {
            _classCallCheck(this, Point);

            this.x = x;
            this.y = y;
        }

        Point.prototype.toString = function toString() {   // (B)
            return "(" + this.x + ", " + this.y + ")";
        };

        return Point;
    })();

参考文献:

wd4blue-Babel 6: loose 模式
2ality-Configuring Babel

支付宝扫码打赏 微信打赏

坚持原创技术分享,您的支持将鼓励我继续创作!

扫描二维码,分享此文章

逆葵's Picture
逆葵

网名逆葵。北邮土著,CS 硕士在读。《Vue.js 权威指南》作者之一。学习、思考、沉淀中, 向成为顶级 JavaScript 技术栈开发者努力。