共计 2847 个字符,预计需要花费 8 分钟才能阅读完成。
Babel 是一个编译器,和其余编译器一样,编译过程分为三个阶段,别离是解析(parsing)、转换(transforming)和生成(generate)。其中剖析和生成阶段由 Babel 外围实现,而转换阶段,则由 Babel 插件实现。所以如果咱们要实现代码的转换,须要为 Babel 增加插件。Babel 也提供了很多的接口来供咱们编写本身的插件来转换咱们的理论代码。
插件的介绍
上面是一个典型的 Babel 插件构造:
export default function({types: babelTypes}) {
return {
visitor: {Identifier(path, state) {},
ASTNodeTypeHere(path, state) {}}
};
};
其中咱们须要关注的内容如下:
- babelType:相似 lodash 那样的工具集,次要用来操作 AST 节点,比方创立、校验、转变等。例如判断某个节点是不是标识符。
- path:AST 中有很多节点,每个节点可能有不同的属性,并且节点之间可能存在关联。path 是个对象,它代表了两个节点之间的关联。咱们能够在 path 上拜访到节点的属性,也能够通过 path 来拜访到关联的节点。
- state:代表了插件的状态,咱们能够通过 state 来拜访插件的配置项。
- visitor:Babel 采取递归的形式拜访 AST 的每个节点,之所以叫做 visitor,只是因为有个相似的设计模式叫做访问者模式,不必在意背地的细节。
- Identifier、ASTNodeTypeHere:AST 的每个节点,都有对应的节点类型,比方标识符、函数申明等,能够在 visitor 上申明同名的属性,当 Babel 遍历到相应类型的节点,属性对应的办法就会被调用,传入的参数就是 path、state。
插件的应用
例如咱们来实现一个简略的插件,将所有名称为 hello 的标识符,转成 xkd。
首先要确保曾经装置了 @babel/cli 依赖,如果没有能够执行下述命令:
npm install --save-dev @babel/cli
而后能够开始创立插件,判断标识符的名称是否是 a,如果是则替换成 xkd,plugin.js 文件内容如下所示:
module.exports = function({types: babelTypes}) {
return {
name: "simple-plugin-replace",
visitor: {Identifier(path, state) {if (path.node.name === 'a') {path.node.name = 'xkd';}
}
}
};
};
而后在 index.js 文件中编写源代码,例如:
var a = 10;
function func(){
var a = 20;
console.log(a);
}
执行如下命令:
npx babel --plugins ./plugin.js index.js
输入的转码后果为:
"use strict";
var xkd = 10;
function func() {
var xkd = 20;
console.log(xkd);
}
能够看到,代码中的所有标识符 a 都被替换成了 xkd。
插件配置
插件能够有本人的配置项。咱们能够批改后面的例子,看下在 Babel 插件中如何获取配置项。
示例:
例如咱们批改 .babelrc 文件中的配置项:
{
"plugins": [ ["./plugin", {
"a": "one",
"b": "two"
}] ]
}
而后批改插件代码,从 state.opts 中获取到配置参数。
module.exports = function({types: babelTypes}) {
return {
name: "simple-plugin-replace",
visitor: {Identifier(path, state) {
let name = path.node.name;
if (state.opts[name]) {path.node.name = state.opts[name];
}
}
}
};
};
在 index.js 文件中写入须要用到的测试代码:
let a = 10;
let b = 20;
运行转码命令 npx babel index.js,输入转码后的后果如下:
let one = 10;
let two = 20;
转译插件
插件能够分为两种,分是转译插件和语法插件,转译插件能够用于转译代码。同一类语法可能同时存在语法插件版本和转译插件版本,如果咱们应用了转译插件,就不必再应用语法插件了,因为转换插件将启用相应的语法插件。
ES3
- member-expression-literals
- property-literals
- reserved-words
ES5
- property-mutators
ES2015
- arrow-functions
- block-scoped-functions
- block-scoping
- classes
- computed-properties
- destructuring
- duplicate-keys
- for-of
- function-name
- instanceof
- literals
- new-target
object-super
`- parameters
- shorthand-properties
- spread
- sticky-regex
- template-literals
- typeof-symbol
- unicode-escapes
- unicode-regex
ES2016
- exponentiation-operator
ES2017
- async-to-generator
ES2018
- async-generator-functions
- dotall-regex
- named-capturing-groups-regex
- object-rest-spread
- optional-catch-binding
- unicode-property-regex
Modules
- modules-amd
- modules-commonjs
- modules-systemjs
- modules-umd
Experimental
- class-properties
- decorators
- do-expressions
- export-default-from
- export-namespace-from
- function-bind
- function-sent
- logical-assignment-operators
- nullish-coalescing-operator
- numeric-separator
- optional-chaining
- partial-application
- pipeline-operator
- private-methods
- throw-expressions
- private-property-in-object
语法插件
当咱们增加语法插件之后,在解析这一步就使得 Babel 可能解析特定类型的语法。
或者也能够从 Babel 解析器提供任何插件选项,例如 .babelrc 文件中能够像上面这样配置:
{
"parserOpts": {"plugins": ["jsx", "flow"]
}
}