babel是一个工具链,其主要用于将ES5以上对的版本代码转化为向下兼容的JavaScript语法。
本文是针对babel7.10.0讲解的
mkdir babel-test
cd babel-test
npm init
在babel-test
根目录下创建.babelrc
文件,用于配置babel。
babel命令会从当前编译文件依次向上查找,直到找到
.babelrc
或babel.config.js
为止。
在根目录下新建source-code
目录用于存放源码。
在根目录下新建target-code
目录用于存放编译后的代码。
babl核心包,其定义了代码转换的一些方法,可将源代码根据配置转换为兼容环境的目标代码。
npm install --save-dev @babel/core
几乎所有babel相关组件均依赖于它,没有该包,则无法正常执行编译操作。
@babel/cli
是babel提供的内建命令工具,可以在终端使用命令主动编译需要的文件。
npm i --save-dev @babel/cli
@babel/cli
是babel命令工具,不适合全局安装。
@babel/node
是babel对动态编译的支持,适合全局安装,会损失程序速度和性能。他们都依赖于
@babel/core
在source-code
目录下新建test.js
文件,内容如下:
const funA = ()=>{
let arr = ["a","b","c"];
let isExist = arr.includes((item)=>item=="c");
console.log(isExist)
}
funA();
new Promise((resolve)=>{
console.log("异步操作")
resolve();
}).then(()=>{
console.log("异步成功")
})
转换命令
npx babel source-code/test.js --out-file target-code/out.js
将
source-code/test.js
编译后,生成out.js
到target-code
目录下。
此时你会发现out.js
生成了,但是未进行编译转换。因为babel需要根据配置来确认需要转换为什么环境的代码,babel7将babel6中集成的编译功能拆分为了一个个插件,默认情况下无可用插件编译,所以无法执行编译。
babel7是通过一个个插件来进行编译的,如果没有插件则无法进行编译转换。
例如:上面test.js中使用了=>
,箭头函数的编译插件为:@babel/plugin-transform-arrow-functions
npm i --save-dev @babel/plugin-transform-arrow-functions
修改执行命令为:
npx babel source-code/test.js --out-file target-code/out.js --plugins=@babel/plugin-transform-arrow-functions
此时你可以发现
=>
已经转换为了function()
。当然一段代码的编译将使用到很多的插件,例如:
- 将const,let转换为var:
@babel/plugin-transform-block-scoping
- 将class转化为function:
@babel/plugin-transform-classes
详情请参考:https://www.babeljs.cn/docs/plugins
presets
是插件集合,我们代码中可能用到了多个需要转换的插件,如果一个一个的引入,耗时且难以维护。所以官方提出了一些插件集合来简化我们的引入操作。
常见的插件集有:
@babel/preset-env
:ES2015、ES2016、ES2017的语法插件集
其不支持stage-x插件的转换
@babel/preset-stage-0
:stage-0,1,2,3都是对预设语法的转换插件,越靠前的插件,包含内容越多。
stage-0
包含了1,2,3的所有插件,另外还支持transform-do-expressions
,transform-function-bind
。
Babel7
开始废弃了stage-x
的预设集,请谨慎使用,在babel7种按需导入对应的插件即可。
@babel/plugin-transform-flow-strip-types
:去除ts类型校验
@babel/preset-react
:支持jsx语法的转换
babel-preset-minify
:压缩代码
@babel/preset-typescript
:转换ts代码
npm install --save-dev @babel/preset-env
在.babelrc中添加如下信息:
{
"presets": [
"@babel/preset-env"
],
"plugins": []
}
执行编译:
npx babel source-code/test.js --out-file target-code/out.js
可发现除了
Promise
,代码基本被编译了。
@babel/polyfill
由core-js2
和regenerator-runtime
组成,用来实现对generator
、async
、Promise
、Symbol
等函数的支持。babel
默认进队对js语法进行解析,并不能转换API。
npm i --save-dev @babel/polyfill
在入口文件头部引入@babel/polyfill
,即可按需加载文件中用到的api。
使用方法
修改.babelrc
配置
{
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "usage", //手动引入@babel/polyfill,如果设置为entry则可在配置在entry上,自动引入
"corejs": "3" //指定corejs的版本,否则会有警告
}
]
],
"plugins": []
}
在test.js
头部添加import "@babel/polyfill";
执行编译后代码如下:
"use strict";
require("core-js/modules/es.array.includes");
require("core-js/modules/es.object.to-string");
require("core-js/modules/es.promise");
var funA = function funA() {
var arr = ["a", "b", "c"];
var isExist = arr.includes(function (item) {
return item == "c";
});
console.log(isExist);
};
funA();
new Promise(function (resolve) {
console.log("异步操作");
resolve();
}).then(function () {
console.log("异步成功");
});
可以看到自动引入了
promise
、includes
、to-string
三个api。
@babel/runtime
依赖于@babel/helpers
和regenerator-runtime
。
@babel/helpers
:包含了很多辅助方法,可以便于将高阶语法转化为地接语法,相当于公共函数类。例如class转换_classCallCheck
。
regenerator-runtime
:生成器函数、async
、await
经babel编译后,regenerator-runtime
模块用于提供功能实现。
@babel/runtime
解决了2个问题:
@babel/runtime
抽离了公共方法,存放在@babel/helpers
中,减少了代码体积。@babel/polyfill
虽按需引入了,但是会污染全局命名空间,当用户自定的方法和公共库方法重名时,就会造成冲突。npm i --save-dev @babel/plugin-transform-runtime
npm i --save @babel/runtime
npm install --save @babel/runtime-corejs2
@babel/runtime
需搭配@babel/plugin-transform-runtime
使用。
@babel/plugin-transform-runtime
的corejs默认为false,即不对polyfill进行处理,可设置为不同的版本让其支持。
执行编辑后可以发现,对polyfill
引入内容添加了别名,这样就避免了全局污染。
@babel/register
提供了动态编译,使我们的源码可以直接在生产环境运行,而不是先编译后生成环境才能执行。
npm install --save-dev @babel/register
使用时,在入口文件头部引入require("@babel/register");
,即可实现动态编译,不建议使用,因为损耗生成环境性能。
Babel7.10.0
开始正式废弃了预设提案stage-x
的支持需要我们手动按需在插件中依次引入。
"plugins": [
// Stage 0
"@babel/plugin-proposal-function-bind", //::修饰符转化为bind或者call
// Stage 1
"@babel/plugin-proposal-export-default-from", //export * form语法支持
"@babel/plugin-proposal-logical-assignment-operators", //||=,&&=等合并操作符的支持
["@babel/plugin-proposal-optional-chaining", { "loose": false }],//?.属性名 链式结构的支持
["@babel/plugin-proposal-pipeline-operator", { "proposal": "minimal" }], //管道链接器|>的支持
["@babel/plugin-proposal-nullish-coalescing-operator", { "loose": false }], //?? 操作符的支持
"@babel/plugin-proposal-do-expressions", //do{...} 表达式的支持
// Stage 2
["@babel/plugin-proposal-decorators", { "legacy": true }], //注解修饰器的支持
"@babel/plugin-proposal-function-sent", //function* generator函数别名的支持
"@babel/plugin-proposal-export-namespace-from", //export * as name form语法支持,即导出别名的支持
"@babel/plugin-proposal-numeric-separator", //**,>>,<<等数学运算符的支持
"@babel/plugin-proposal-throw-expressions", //throw new Error(),异常抛出函数的支持
// Stage 3
"@babel/plugin-syntax-dynamic-import", //按需导入的支持,目前可以用polyfill代替API的导入
"@babel/plugin-syntax-import-meta",
["@babel/plugin-proposal-class-properties", { "loose": false }],//class类解析
"@babel/plugin-proposal-json-strings" //字符串中添加固定字符
]
antd样式按需加载
"plugins": [
"@babel/plugin-syntax-dynamic-import",
["import", { "libraryName": "antd", "libraryDirectory": "lib", "style": "css"}, "ant"],
["import", { "libraryName": "antd-mobile", "libraryDirectory": "lib", "style": "css"},"antd-mobile"]
]
@babel/plugin-proposal-object-rest-spread
:支持{…a},解构语法,使用较多。
自动替换stage-x插件
npx babel-upgrade --write
运行该命令会自动更新
package.json
和.babelrc
文件,替换package.json中出现的stage-x
预设集。
babel
转换分为3个步骤:词法解析,逻辑转换,代码生成。
一个插件就是一个函数,其接受一个babel对象,返回一个包含visitor
的对象,visitor
就是一颗AST语法树。
新建source-code/my-plugin.js
,内容如下:
module.exports = function (babel) {
const t = babel.types;
return {
name: "myPlugin",
visitor: {
CallExpression(path) {
const obj = path.node.callee.object //对象
const prop = path.node.callee.property //属性
const arguments = path.node.arguments //参数
if (t.isIdentifier(obj) && t.isIdentifier(prop) && obj.name === 'console' && prop.name === 'log') {
const location = `****location:row:${path.node.loc.start.line}, column:${path.node.loc.start.column}****`;
//为每个console.log()语句的末尾加上一个位置信息
arguments.push(t.stringLiteral(location))
}
}
}
}
}
这里我们定义了一个插件,为每个console.log增加了一个参数,显示打印位置。
使用
在.babelrc
中添加上诉插件:
"plugins": [
"./source-code/my-plugin.js"
]
执行编译,可发现console.log("异步操作")
=>console.log("异步操作", "****location:row:10, column:4****")
自定义插件就是操作AST语法树,使其满足自己的需求,然后重新生成新的代码。