Babel使用说明

翻译链接:https://babeljs.io/docs/en/usage

Babel本身自带了一些工具,便于用户使用。下面会给一个关于这些工具的概括。

概述

这份使用说明是展示如何将ES6+的语法转化为目前浏览器能识别的代码。这个步骤会涉及到转化新语法和一些缺失方法的填充(polyfill)。

设置整个过程需要如下:

  1. 运行下面命令,安装包。

    1
    2
    npm install --save-dev @babel/core @babel/cli @babel/preset-env
    npm install --save @babel/polyfill
  2. 创建babel.config.js文件在你的根目录下。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    const presets = [
    [
    "@babel/env",
    {
    targets: {
    edge: "17",
    firefox: "60",
    chrome: "67",
    safari: "11.1",
    },
    useBuiltIns: "usage",
    },
    ],
    ];

    module.exports = { presets };
  3. 运行下面命令,将代码从src目录下编译到lib目录下。

    1
    ./node_modules/.bin/babel src --out-dir lib

CLI的基本用法

自从Bable 7之后,所有需要的Babel模块,都是npm独立的包发布在域名@babel下。这样做,对于一些特殊的案例下,会比较灵活。接下来,我们看一下@babel/core@babel/cli

Core

有关Babel的核心功能,都被放到了@babel/core模块下。你可以直接在JS代码中直接require进来。

1
2
const babel = require("@babel/core");
babel.transform("code", optionsObject);

做为使用者,你可能需要安装其它工具,做为接口和@babel/core一起使用,并集成在你的开发流程中。尽管如此,你仍然会需要查看它的文档,学习options。

Core tool

@babel/cli允许你在终端使用babel。下面是安装命令和基本用法例子:

1
2
npm install --save-dev @babel/core @babel/cli
./node_modules/.bin/babel src --out-dir lib

他会解析你在src下的JS,经过一系列的转化,在lib目录下输出。由于,我们没有命令它做任何转化,最后的输出会和输入一致。我们可以通过设置option来命令babel做一些转化。
例子中,我们使用了--out-diroption。你可以通过--help来查看cli还支持哪些option。对于我们来说,比较重要的两个option是:plugins和presets。

Plugins & Presets

转化以Plugins的形式出现。这些插件都是小的JS程序,指导Babel转化代码。你也可以创建自定义Plugins将转化应用在代码上。将ES6+语法转化成ES5,我们可以利用官方插件:@babel/plugin-transform-arrow-functions

1
2
npm install --save-dev @babel/plugin-transform-arrow-functions
./node_modules/.bin/babel src --out-dir lib --plugins=@babel/plugin-transform-arrow-functions

这样,任何箭头函数都会转化成ES5的兼容函数:

1
2
3
4
5
const fn = () => 1;
// converted to
var fn = function fn() {
return 1;
};

同样,ES6+除了箭头函数的语法外,还有别的。我们不可能为这些语法,一个一个的添加插件。Babel提供给我们Preset属性。这个属性是一个预先定义好的插件集。

和Plugins一样,你可以创建属于自己的Preset。在我们例子中,有一个非常不错的preset:env

1
2
npm install --save-dev @babel/preset-env
./node_modules/.bin/babel src --out-dir lib --presets=@babel/env

不需要其它的配置,env已经包括了将ES6+转化成当前浏览器支持的版本的所有的插件。presets同样可以有option。不是从终端配置option,接下来我们看下从配置文件中,如何配置option。

配置

基于需求,会有多种方式配置。可以阅读Configure Babel了解更多信息。

现在,创建一个babel.config.js的文件,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const presets = [
[
"@babel/env",
{
targets: {
edge: "17",
firefox: "60",
chrome: "67",
safari: "11.1",
},
},
],
];

module.exports = { presets };

现在,env会下载那些现在浏览器无法正常使用的插件,转化代码。以上,我们所做的都是转化语法。接下来,让我们看下polyfill。

Polyfill

@babel/polyfill包括core-js和一个自定义的`regenerator runtime`来模拟一个ES6+的环境。

这就意味着,你可以用内置的语法,比如Promise或者WeakMap,静态方法比如Array.from或者Object.assign,原型方法比如Array.prototype.includes,和generator函数(需要用regenerator插件)。Polyfill会被添加到全局作用域下。

对于库/工具开发者而言,Polyfill提供的太重。如果你不使用类似Array.prototype.includes原型方法,你可以利用transform runtime,代替@babel/polyfill,避免污染全局环境。因为plugin-transform-runtime提供了一个方法到core-js的一个别名。你可以直接用这个插件,无需再polyfill。如下:

1
2
3
4
// in
var sym = Symbol();
var promise = new Promise();
console.log(arr[Symbol.iterator]());

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// out
"use strict";
var _getIterator2 = require("@babel/runtime-corejs2/core-js/get-iterator");
var _getIterator3 = _interopRequireDefault(_getIterator2);
var _promise = require("@babel/runtime-corejs2/core-js/promise");
var _promise2 = _interopRequireDefault(_promise);
var _symbol = require("@babel/runtime-corejs2/core-js/symbol");
var _symbol2 = _interopRequireDefault(_symbol);
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : { default: obj };
}
var sym = (0, _symbol2.default)();
var promise = new _promise2.default();
console.log((0, _getIterator3.default)(arr));

如果你确切知道哪些功能需要你polyfill,你可以直接从core-jsrequire他们。

幸运的是,env preset提供了一个选项useBuiltIns给我们。当我们将它设置成"usage"时,env就会应用最新的优化。这些优化包括了你需要的polyfill。配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const presets = [
[
"@babel/env",
{
targets: {
edge: "17",
firefox: "60",
chrome: "67",
safari: "11.1",
},
useBuiltIns: "usage",
},
],
];

module.exports = { presets };

基于上面的配置,Babel可以检测你目标环境需要转化的语法问题和一些polyfill的内置方法。比如下面代码:

1
Promise.resolve().finally();

就会转化成(因为edge 17没有Promise.prototype.finally方法):

1
2
require("core-js/modules/es.promise.finally");
Promise.resolve().finally();

如果我们没有设置envuseBuiltInsusage,我们必须在入口文件头部require整个polyfill进来。

总结

我们利用@babel/cli将babel运行在终端,@babel/polyfill为JS的一些特征填充。env预设转化和填充目标浏览器没有的功能。