Skip to content

Babel7 使用配置详解 #1

@lilins

Description

@lilins

0. Babel 是什么?

Babel是编译器,用来转换 ECMAScript 2015 标准及以上(ES6)的代码。简单来说就是可以帮助你把 ES6/7/8 的语法编译成当前环境可用的代码。

1. Babel 怎么用?

有以下三种方法使用,前两种是通过 Node 命令行使用,第三种是配合第三方打包工具,一般会使用第三种方式,但前两种对于理解 Babel 很有帮助,所以先放在前面。

1.1 CLI

首先安装 @babel/core@babel/cli@babel/core 是分析 ES6 语法,并使用插件进行转换的工具,@babel/cli 则是 babel-cli 的前身。需要说明的是,从 Babel7 开始就把 babel-cli 放入到 @babel 包当中去了,如果单独安装了 babel-cli 并且通过 babel 命令来运行,可能会出现问题。

此外,我们还需要安装 Plugin(插件) 插件是 babel 编译的重要组成部分,下文会提到。

npm install --save-dev @babel/core @babel/cli
npm install --save-dev @babel/plugin-transform-arrow-functions

我们选择的插件是用于转换 箭头函数 的插件(plugin-transform-arrow-functions),接着,我们尝试在根目录新建 src 目录,在里面新建一个 index.js 文件:

var arrow = a => a;

最后运行命令:

./node_modules/.bin/babel --plugins @babel/plugin-transform-arrow-functions src

由于 @babel/cli 安装在 @babel 包中,所以使用 babel 命令需要去 node_modules 中调用,可以看到结果:

var arrow = function (a) {
  return a;
};

此外,我们还可以加上 --out-dir dist 参数,让转换后的文件输出到 dist 文件夹下。

1.2 Node API

Node API 是使用 @Babel/core 自有的 transform() 方法进行语法转换

首先创建 index.js 文件:

const babel = require("@babel/core");

const code = a => a;

babel.transform(code, {
  plugins: [
    "@babel/plugin-transform-arrow-functions"
  ]
}, function(err, result){
  console.log(result.code)
});

运行命令:

node index.js

可以看到结果:

(function (a) {
  return a;
});

1.3 配合webpack等第三方打包工具

下文会提到,这里先略过。

2. Baebl 配置文件

我们可以看到在使用 Babel 的两种方法中,都存在 plugins@babel/plugin-transform-arrow-functions,这都是 Babel 的配置,对于 Babel 的配置有两种方式:

2.1 .babelrc (推荐使用)

官方推荐在根目录新建 .babelrc 文件来配置Babel:

{
  "plugins": [
    "@babel/plugin-transform-arrow-functions"
  ]
}

2.2 babel.config.js

在 Monorepo 结构中推荐使用,在不同的 Package 目录下新建 babel.config.js 文件来写入配置文件:

const plugins = [
  "@babel/plugin-transform-arrow-functions"
];

module.exports = { plugins };

我们可以通过第一种方式来验证两种配置文件,直接运行命令,不需要加额外的配置,babel 会自动寻找 .babelrcbabel.config.js

$user>./node_modules/.bin/babel src
var arrow = function (a) {
  return a;
};

需要注意,babel.config.js 文件的优先级比 .babelrc 文件高,如果同时存在两个文件,会优先读取 babel.config.js

2.3 .babelrc VS babel.config.js

两者如何选择?

当项目非常大,各个模块之间的 babel 配置都各不相同,想要通过编程动态生成配置,babel.config.js 更适合你。

当你的项目不大,配置文件也不多(甚至只有一个)的情况下,.babelrc 更适合你。

2.4 package.json

除了以上两种主要配置方式之外,还可以在 package.json 文件中直接写入 babel 配置

{
  "name": "my-package",
  "version": "1.0.0",
  "babel": {
    "plugins": [ ... ],
  }
}

2.5 .babelrc.js

和配置 .babelrc 文件的方法相同,不过你可以用 JavaScript 来写配置。

const plugins = [ ... ];

module.exports = { plugins };

3. Babel 配置文件中的 pluginspresets

上一节介绍到配置中的 plugins, 其实除了 plugins 还有 presets,下面一一来看:

3.1 Plugin(插件)

插件一般是单独的某个新特性,你可以通过安装插件来使用这个新特性:

  • @babel/plugin-transform-arrow-functions: 转换箭头函数的语法
  • @babel/plugin-transform-classes: 使用 Class 语法
  • @babel/plugin-transform-for-of: 使用 for...of 语法

但是 ES6 的特性有很多,通常情况下,很多特性都会在编程中使用到,把这些插件一一加入到配置文件中显然太费事了,所以 Babel 推出了一个“懒人包” presets

3.2 Preset(预置)

Preset 一般会将大部分特性打包,官方的Preset有:

  • @babel/preset-es2015
  • @babel/preset-es2016
  • @babel/preset-es2017
  • @babel/preset-es2018
  • @babel/preset-stage-0
  • @babel/preset-stage-1
  • @babel/preset-stage-2
  • @babel/preset-stage-3
  • @babel/preset-env
  • @babel/preset-react
  • @babel/preset-typescript
  • ...

那么多预置包里都包含了什么?实际应用中应该使用哪种?首先,我们可以来区分一下:@babel/preset-es2015~2018 ,这个系列代表了不同年份 Babel 所推出的不同特性的预置包,比如 @babel/preset-es2015 包含了如下插件:

  • arrow-functions
  • block-scoped-functions
  • block-scoping
  • classes
  • computed-properties
  • destructuring
  • duplicate-keys
  • for-of
  • function-name
  • ...

详细插件情况可点击查看:ES2015

如果想要使用 @babel/preset-es2015 所有插件的话,可以直接使用 @babel/preset-es2015 这个 Preset(预置包)。

此外,@babel/preset-stage-0 数组系列代表了 JavaScript 不同语法的提案阶段所包含的插件,数字越小,包含的新插件越多,也即,stage-0 包含了 stage-1, stage-2, stage-3 三者的插件。

但是!!!

但是!!!

但是!!!

Babel7 已经放弃了所有的年份以及 stage 的 Preset ,并用 @babel/preset-env 取而代之,因为 @babel/preset-env 中已经包含了 2015~2018 以及 stage 的所有内容,所以官方推荐使用。

安装 @babel/preset-env:

npm install --save-dev @babel/preset-env

写入配置 .babelrc 文件:

{
  "presets": [
    "@babel/preset-env"
  ]
}

除了 @babel/preset-env 这个基本包之外,还有用于转换 React, TypeScript 的 @babel/preset-react, @babel/preset-typescript 等各种语法转换插件。

4. 详细配置

在明白了 pluginspresets 之后,我们可以继续深入一些更加详细的配置:

  • loose: 默认值 false, Babel 默认使用 strict 模式;
  • debug: 默认值 false, 调试模式,会打印转换时的一些信息;
  • targets: 当前项目所支持的浏览器配置,这里一般会使用 Browserslist 这个库的查询条件。
  • useBuiltIns: 默认值 false,可填写:"usage" | "entry" | false, 配合 @babel/polyfill 使用,后面会提到。

下面是一个配置项的例子:

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "loose": true,
        "debug": true,
        "targets": {
          "browsers": ["last 2 versions", "ie >= 9"]
        }
      }
    ]
  ]
}

这个配置文件表示使用了 @babel/preset-env 预置包,宽松模式,开启debug,支持除 IE 外所有浏览器最新的两个版本,IE 浏览器支持 9 以上的版本。

点击查看example for 4-babel-preset-env

5. @babel/polyfill VS @babel/plugin-transform-runtime

@babel/preset-env 中虽然包含了大部分 ES6 的新特性,但是有一些特性例如 Promise, Map, Set 等都无法使用,为了能够使用所有的特性,我们需要引入一些补充的工具库,@babel/polyfill@babel/plugin-transform-runtime 就是为了解决这个问题的两种方式。

@babel/polyfill 的方式是通过污染全局方法来获得特性,它会将 Promise, Map, Set 这些方法挂载到 Global 对象上去,使得用户可以使用,这种方法只需要注入一次,就可以在项目的各个地方使用,但是缺点也显而易见,就是会污染全局对象,由于一次性引入所有的特性,包的体积也会相应增加。

@babel/plugin-transform-runtime 特点就是按需引入,它会根据你代码中使用到的新特性引入相应的包,不额外增加包的体积。缺点就是编译速度会变慢,因为每次都需要重复检测使用到的模块,此外,有一些 Array 的方法,例如 includes, filter, fill 无法使用。详情可参考:babel-plugin-transform-runtime

5.1 @babel/polyfill

安装,polyfill 会运行在你的源码之前,所以需要将其放入 dependency, 而不是 devDependency

npm install --save @babel/polyfill

在代码之前引入,一般使用 require 引入,但如果你使用了 ES6 模块,应该替换为 import 方式。

require("@babel/polyfill");
// or
import "@babel/polyfill";

但你也可以不引入,这就需要使用到 useBuiltIns

  • useBuiltIns: "usage": 不需要额外配置 @babel/polyfill,也不需要事先引入,@babel/polyfill 会自动安装 @babel/polyfill
  • useBuiltIns: "entry": 不需要额外配置 @babel/polyfill,但需要在文件入口引入 @babel/polyfill,使用 require 或者 import
  • useBuiltIns: false: 需要额外在配置文件加入 @babel/polyfill 配置

下面来看一下不同的配置文件:

5.1.1 useBuiltIns: "entry"

.babelrc:

{
  "presets": [
    [
      "@babel/preset-env",
      { "useBuiltIns": "entry" }
    ]
  ]
}

index.js:

import '@babel/polyfill';
...

点击查看example for 5.1.1-babel-polyfill-entry

5.1.2 useBuiltIns: "usage"

.babelrc

{
  "presets": [
    [
      "@babel/preset-env",
      { "useBuiltIns": "usage" }
    ]
  ]
}

点击查看example for 5.1.2-babel-polyfill-usage

5.2 @babel/plugin-transform-runtime

安装在 development 环境中,同时 @babel/runtime 也需要被安装:

@babel/runtime 引用了 core-js, helpers, regenerator,core-js 只包含了 helpers,而 helpers 包含了 Class 等特性;regenerator 则是实现了 generator/yeildasync/await

官方文档表示,@babel/runtime 转换插件负责三个任务:

  • 当你使用 generators/async,自动引入 @babel/runtime/regenerator
  • 根据需要可以动态引入 core-js,取代原来引入全部代码的做法
  • 移除内联的 Babel helpers 代码,并使用模块化引入 @babel/runtime/helpers

所以需要注意的是,原本 corejs 还包含 Promise, Map, Set 等特性,但在 Babel7 中只包含了helpers,如果需要使用 Promise, Map, Set 还需要引入 corejs2

npm install --save-dev @babel/plugin-transform-runtime
npm install --save @babel/runtime
npm install --save @babel/runtime-corejs2

.babelrc:

{
  "plugins": ["@babel/plugin-transform-runtime"]
}

5.2.1 详细配置

  • corejs: 默认值 false,可填写 2,表示使用 corejs2corejs2 包含了 Promise, Map, Set等特性;
  • helpers: 默认值 true,会使用引入模块化代码的方式,如果是 false 则是使用内联代码的方式;
  • regenerator: 默认值 true,如果值为 false 的时候会将方法挂载到 Global 对象上,污染全局变量;
  • useESModules: 默认值 false,如果为 true 的时候,将不会使用 commonjs 的方法来加载,这个对某些打包工具例如 webpack 来说,能够打出更小的包来。

5.2.2 配置举例

如果想要使用 Class 以及 async/await

{
  "presets": [
    "@babel/env"
  ],
  "plugins": ["@babel/plugin-transform-runtime"]
}

如果想要使用 Promise

.babelrc:

{
  "presets": [
    "@babel/env"
  ],
  "plugins": [
-   ["@babel/plugin-transform-runtime"],
+   ["@babel/plugin-transform-runtime", {
+     "corejs": 2,
+   }],
  ]
}

点击查看example for 5.2.2-babel-transform-runtime-corejs2

6. webpack

我们通过简单配置 webpack 就可以搭配使用 babel。

首先安装 webpack 以及 babel-loader

npm install --save-dev webpack webpack-cli babel-loader

然后配置 webpack.config.js

const path = require('path');

module.exports = {
  mode: "development",
  entry: path.resolve(__dirname, './index'),
  output: {
    path: path.resolve(__dirname),
    filename: "bundle.js"
  },
  module: {
    rules: [
      {
        test: /\.jsx?$/,
        loader: "babel-loader"
      }
    ]
  } 
}

index.js

var a = b => b
var pr = new Promise((resolve, reject) => {
  resolve(1)
})
pr.then(re => console.log(re))

var map = new Map();

class C {}

function *foo(){}

根据不同的 .babelrc 配置文件,可以得出如下结果:

编号 polyfill useBuiltIns transform-runtime corejs helpers regenerator Size
1 entry 446 KiB
2 usage 131 KiB
3 2 true true 148 KiB
4 2 false true 147 KiB
5 2 true false 122 KiB

点击查看

7. 参考

  1. 官方文档
  2. 你真的会用 Babel 吗?
  3. Babel7 更新

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions