-
Notifications
You must be signed in to change notification settings - Fork 0
Description
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 会自动寻找 .babelrc 或 babel.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 配置文件中的 plugins 和 presets
上一节介绍到配置中的 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. 详细配置
在明白了 plugins 和 presets 之后,我们可以继续深入一些更加详细的配置:
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/polyfilluseBuiltIns: "entry": 不需要额外配置@babel/polyfill,但需要在文件入口引入@babel/polyfill,使用require或者importuseBuiltIns: 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/yeild, async/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,表示使用corejs2,corejs2包含了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 |
点击查看:
- example for 5.1.1-babel-polyfill-entry
- example for 5.1.2-babel-polyfill-usage
- example for 5.2.1-babel-transform-runtime-corejs2