Skip to content

babel 的 preset 和 js 的标准提案 #5

Description

@inetfuture

引言

babel 从 6 开始有 preset 的概念,用来组织繁杂的插件,其实就相当于快捷方式,帮助你快速指定需要的插件组合。比如 babel-preset-react,只需要指定这一个 preset,就可以使用 react 开发所需的插件,包括 babel-plugin-syntax-jsx 等。

preset 和 js 的标准提案

除了 react preset 外,官方维护 preset 还有这些:

  • flow:包含 flow type 所需插件
  • es2015:包含 es2015 语法标准所有相关插件
  • es2016:包含 es2016 语法标准所有相关插件
  • es2017:包含 es2017 语法标准所有相关插件
  • latest:包含从 2015 开始历年语法标准所有相关插件
  • env:在 latest 基础上提供环境配置能力,比如可以配置只支持某一个浏览器的某几个版本,会自动按需启用、禁用插件
  • stage-0:包含处于标准提案 stage 0 阶段的语法所有相关插件
  • stage-1:包含处于标准提案 stage 1 阶段的语法所有相关插件
  • stage-2:包含处于标准提案 stage 2 阶段的语法所有相关插件
  • stage-3:包含处于标准提案 stage 3 阶段的语法所有相关插件

那么 es2015, stage-0 等等这些之间到底是什么关系呢?

js 有很长的历史,中间有多年停滞不前,不但没有任何改进,还因为浏览器大战导致极度混乱。但是随着互联网繁荣,前端对 js 越来越倚重,再加上服务端 node.js 兴起,促使社区成立了标准委员会,开始推进 js 的升级,这个委员会叫做 TC39。

他们负责讨论和改进标准提案,最终通过后,会将多个提案整理到某一年的发布中。es2015 就是第一个发布。

中间每一个提案,都要依次经过 stage 0、stage 1、stage 2、stage 3、stage 4 五个阶段。其中到达 stage4 的为已通过,会出现在下一年的发布中。到达 stage3 的属于比较成熟的,极有可能在较小改动的前提下通过并发布,stage3 之前的则不太成熟,stage 越往前越容易大幅改动甚至被完全去掉。所以对于开发者来说,使用 stage 3 之前的语法有一定风险。

知道了这些,babel 的这些 preset 也就不难理解,es20xx 的,都是已经正式通过并在那一年发布的,stage-x 的都是还在讨论中,不太稳定的。

在这里可以查看各个提案的状态:

我们以 Rest/Spread Properties 为例,该提案处在 stage 3,所以如果你想使用包含它的 preset,应该使用 babel-preset-stage-3(当然你始终可用直接指定插件 babel-plugin-transform-object-rest-spread 而非 preset)。

我之前用的时候看到 babel-preset-latest,以为它包含了所有的,实际它只包含了所有正式发布的。😢

示例

babel 的最佳实践是使用 babel-preset-env,示例配置文件 .babelrc 如下:

{
  "presets": [
    ["env", {
      "targets": {
        "node": "current",
        "browsers": "last 2 versions"
      }
    }]
  ]
}

相当于声明“我需要支持当前使用的 node.js 版本,以及主流浏览器的最近两个版本”,该 preset 会自动启用先关插件以满足这个需求。

假设我们当前使用的 node.js 版本是 v7.7.4,有如下代码 demo.js

import { MongoClient } from 'mongodb';

async function run() {
    const db = await MongoClient.connect('mongodb://localhost/test');
    console.log(db);
}

run();

执行 babel demo.js 得到编译结果:

'use strict';

var run = function () {
    var _ref = _asyncToGenerator(regeneratorRuntime.mark(function _callee() {
        var db;
        return regeneratorRuntime.wrap(function _callee$(_context) {
            while (1) {
                switch (_context.prev = _context.next) {
                    case 0:
                        _context.next = 2;
                        return _mongodb.MongoClient.connect('mongodb://localhost/test');

                    case 2:
                        db = _context.sent;

                        console.log(db);

                    case 4:
                    case 'end':
                        return _context.stop();
                }
            }
        }, _callee, this);
    }));

    return function run() {
        return _ref.apply(this, arguments);
    };
}();

var _mongodb = require('mongodb');

function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }

run();

编译结果是较复杂的,主要是因为现在主流浏览器还不支持 async/await,babel 做了很多工作。实际上即便如此编译出来,也是不能直接在浏览器跑的(不考虑我这里用 mongodb 的库作为例子),还需要引入 generator 运行时。

如果我们把上面配置中的 "browsers": "last 2 versions" 去掉,只留下 "node": "current",编译结果则简单许多,因为我们所使用的 v7.7.4 版 node.js 已经原生支持 async/await,babel 不再需要额外处理,如下:

'use strict';

var _mongodb = require('mongodb');

async function run() {
    const db = await _mongodb.MongoClient.connect('mongodb://localhost/test');
    console.log(db);
}

run();

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions