你是否也对项目里引入的这样那样的 babel 库感到头晕?如 babel-core
,@babel/preset-env
,那就一起来实践看看吧。
@babel-core
首先是 babel-core,其中包含了将代码字符串转换为 ast 等核心功能。
// babel.js
const fs = require('fs');
const babel = require('@babel/core');
const source = fs.readFileSync('src/before.js', 'utf8'); // 读取代码文件
const result = babel.transformSync(source, { // 对代码字符串进行转换
ast: true,
});
// src/before.js
const sayHello = (name) => `Hello, ${name}`;
转换后的 AST 内容如下:
{
"type": "File",
"start": 0,
"end": 45,
"loc": {
"start": { "line": 1, "column": 0 },
"end": { "line": 2, "column": 0 }
},
"errors": [],
"program": {
"type": "Program",
"start": 0,
"end": 45,
"loc": {
"start": { "line": 1, "column": 0 },
"end": { "line": 2, "column": 0 }
},
"sourceType": "module",
"interpreter": null,
"body": [
{
"type": "VariableDeclaration",
"start": 0,
"end": 44,
"loc": {
"start": { "line": 1, "column": 0 },
"end": { "line": 1, "column": 44 }
},
"declarations": [
{
"type": "VariableDeclarator",
"start": 6,
"end": 43,
"loc": {
"start": { "line": 1, "column": 6 },
"end": { "line": 1, "column": 43 }
},
"id": {
"type": "Identifier",
"start": 6,
"end": 14,
"loc": {
"start": { "line": 1, "column": 6 },
"end": { "line": 1, "column": 14 },
"identifierName": "sayHello"
},
"name": "sayHello"
},
"init": {
"type": "ArrowFunctionExpression",
"start": 17,
"end": 43,
"loc": {
"start": { "line": 1, "column": 17 },
"end": { "line": 1, "column": 43 }
},
"id": null,
"generator": false,
"async": false,
"params": [
{
"type": "Identifier",
"start": 18,
"end": 22,
"loc": {
"start": { "line": 1, "column": 18 },
"end": { "line": 1, "column": 22 },
"identifierName": "name"
},
"name": "name"
}
],
"body": {
"type": "TemplateLiteral",
"start": 27,
"end": 43,
"loc": {
"start": { "line": 1, "column": 27 },
"end": { "line": 1, "column": 43 }
},
"expressions": [
{
"type": "Identifier",
"start": 37,
"end": 41,
"loc": {
"start": { "line": 1, "column": 37 },
"end": { "line": 1, "column": 41 },
"identifierName": "name"
},
"name": "name"
}
],
"quasis": [
{
"type": "TemplateElement",
"start": 28,
"end": 35,
"loc": {
"start": { "line": 1, "column": 28 },
"end": { "line": 1, "column": 35 }
},
"value": { "raw": "Hello, ", "cooked": "Hello, " },
"tail": false
},
{
"type": "TemplateElement",
"start": 42,
"end": 42,
"loc": {
"start": { "line": 1, "column": 42 },
"end": { "line": 1, "column": 42 }
},
"value": { "raw": "", "cooked": "" },
"tail": true
}
]
}
}
}
],
"kind": "const"
}
],
"directives": []
},
"comments": []
}
@babel/plugin-transform-arrow-functions
代码的转换均由 plugin 来完成,如 plugin-transform-arrow-functions 的功能是将箭头函数转换为普通的函数,这给了 babel 很大的扩展性。
const result = babel.transformSync(source, {
plugins: ['@babel/plugin-transform-arrow-functions'], // 引入 plugin
ast: true,
});
转换前后的代码:
// 转换前
const sayHello = (name) => `Hello, ${name}`;
// 转换后
const sayHello = function (name) {\n return `Hello, ${name}`;\n};
nice~ 👍
我们甚至可以编写自己的 babel plugin,来实现我们想要的代码转换。如编写一个 plugin,来将 const
转换为 let
。
// plugin.js
module.exports = function () {
return {
visitor: {
VariableDeclaration(path) {
const node = path.node;
node.kind = node.kind === 'const' ? 'let' : node.kind;
},
},
};
};
// babel.js
const result = babel.transformSync(source, {
plugins: ['./plugin']
});
// 输出结果
"code": "let sayHello = name => `Hello, ${name}`;"
@babel/cli
提供命令行操作。
安装后在 package.json 中添加命令:
"scripts": {
"babel": "babel src/index.js --out-file output/index.js" //指定输入和输出
},
添加一个 babel.config.json 来定义 babel 配置:
{
"plugins": ["@babel/plugin-transform-arrow-functions"]
}
运行 npm run babel
或 yarn babel
即可看到输出结果:
// output/index.js
const sayHello = function (name) {
return `Hello, ${name}`;
};
@babel/preset-env
一个 babel plugin 一般只专注做一件事,为了完成一个复杂功能,有时我们需要引入一组 plugin。babel 配置中有一个 presets
的选项,可以将它当作一组插件的集合。
比如 “@babel/preset-env”,它的功能非常强大。它默认支持所有现代 JavaScript 语法的转换,如 ES2015, ES2016 等。
使用时只需在 babel.config.json 添加:
{
"presets": [["@babel/env"]]
}
运行 yarn babel
的输出结果:
"use strict";
var sayHello = function sayHello(name) {
return "Hello, ".concat(name);
};
它可以通过传入需要支持的浏览器 来做对应的语法转换,如:
{
"presets": [
[
"@babel/env",
{
"targets": {
"edge": "17",
"firefox": "60",
"chrome": "67",
"safari": "11.1"
}
}
]
]
}
polyfill
用于模拟一个完整的 ES2015+ 环境,在运行环境缺少新的特性(如 Promise)时,在环境中添加对应的 polyfill。
推荐和 @babel/preset-env
一起使用,它将在遇到目标浏览器不支持的特性时添加对应的 polyfill。这样我们就可以放心使用 ES 的新语法了,nice~👍
{
"presets": [
[
"@babel/env",
{
"targets": {
"edge": "17",
"firefox": "60",
"chrome": "40", // 设置为较低版本测试 polyfill
"safari": "11.1"
},
"useBuiltIns": "usage" // 添加此声明即可
}
]
]
}
添加一个 Promise 用于测试:
// index.js
const sayHello = (name) => `Hello, ${name}`;
Promise.resolve().then(() => {
console.log('Hello World~');
});
// output.js
"use strict";
require("core-js/modules/es6.object.to-string.js");
require("core-js/modules/es6.promise.js"); // 引入 polyfill 文件
var sayHello = function sayHello(name) {
return "Hello, ".concat(name);
};
Promise.resolve().then(function () {
console.log('Hello World~');
});