babel是什么?
一个 JavaScript 编译器,可将 codeA 转换成 codeB。比如通 过 @babel/preset-react,将jsx转换为Vdom的书写形式
// jsx
<div>babel</div>
// 编译后
React.createElement("div", null, "babel");
核心工具
既然是转换代码,我们就需要一些工具,下面介绍下babel工具包
@babel/core
提供babel的核心功能,可将code转化为ast,并生成代码
const babel = require("@babel/core");
babel.transform("code", optionsObject);
@babel/cli
@babel/polyfill
对一些低版本浏览器进行api的polyfill
Promise.resolve().finally();
// 转换后
"use strict";
require("core-js/modules/es.object.to-string.js");
require("core-js/modules/es.promise.js");
require("core-js/modules/es.promise.finally.js");
Promise.resolve()["finally"]();
注意:@babel/polyfill 合拼到了 @babel/cli中,所以不需要单独安装
@babel/plugin-transform-runtime
在代码 polyfill 时,可引入 core.js的依赖进行降级
配置文件
babel.config.json 可配置 babel 转换选项,也可设置成 .babelrc 与 babel.config.js
{
"presets": [
[
"@babel/env",
// 配置编译环境,如typescript,flow等
{
// 与@babel/plugin-transform-runtime + core.js 相似
"useBuiltIns": "usage",
"corejs": "3.6.5"
}
]
],
"plugins": [// babel插件]
}
Preset vs Plugin
Preset
翻译为预设,可根据开发环境配置,在config中的preset中配置
- @babel/preset-env 将es6以及以上版本的代码转换为es5代码
- @babel/preset-typescript 编译 ts
- @babel/preset-react 编译 react
除了正常环境意外,还有功能的stage过程,主要是实验性的一些功能,未审批通过:
该TC39分类的建议分为以下几个阶段:
- 阶段0-Strawman:一个想法,可能是Babel插件。
- 第1阶段-提案:有点搞头。
- 第2阶段-草稿:雏形。
- 第3阶段-候选:完整的规范并开始在浏览器试用。
-
Plugin
比preset更加丰富
如果我们使用过 mob 6 一下版本,肯定会用到装饰器{
"plugins": ["transform-decorators-legacy", "transform-class-properties"]
}
我们需要 transform-decorators-legacy 、 transform-class-properties 来编译装饰器与class语法
所以 Plugins 更像是补充 Preset 缺失的功能,更加轻量灵活工具包
@babel/parser
@babel/generator
@babel/traverse
@babel/types
@babel/register
用来通过绑定 node.js 的 require 自动使用 babel 编译文件
@babel/template
用于从字符串表示的代码构建 AST 节点,简化了使用@babel/types 构建节点的过程。
@babel/helpers
运行过程
源码解析阶段:@babel/parser
- 转换阶段:在这个阶段所有插件使用 @babel/traverse 遍历 AST,遍历过程中可以对节点进行一些自定义操作
- 目标代码生成阶段:@babel/generator
实现一个 babel Plugin
现在我们实现一下 babel-plugin-replace,主要功能是在开发中通过缩写的形式来替换一些变量书写,比较经典的应用是利用缩写在判断环境变量
webpack 配置如下 ```javascript // webpack.config.js const path = require(“path”);
module.exports = {
entry: “./index.js”,
mode:’production’,
output: {
path: path.resolve(dirname, “dist”),
filename: “bundle.js”,
},
module: {
rules: [
{
test: /.js$/,
exclude: /node_modules/,
use: {
loader: “babel-loader”,
options: {
plugins: [
[
“./babel-plugin”,
{
replace:{
// 区别开发环境
Dev: process.env.NODE_ENV === 'development'
,
Prod: process.env.NODE_ENV === 'production'
,
TEST__: process.env.NODE_ENV === 'test'
,
}
},
],
],
},
},
},
],
},
};
我们可以更加不同的变量环境来判断是否打包
```javascript
// index.js
if(__Dev__){
console.log('开发环境')
}
if(__PROD__){
console.log('开发环境')
}
if(__TEST__){
console.log('测试环境')
}
我们可以通过 https://astexplorer.net/ 来查看ast的情况如下
通过 ast 看查到 Identifier 下 name 属性值是需要替换的
代码实现步骤
- 在外部配置中获取配置项
- 如果存在配置项,我们进行替换,反之直接返回
- 拿到遍历 Identifier 节点,获取对应 name 属性值进行匹配,如果匹配进行节点属性值替换 ```javascript function visitor(path, state) { // opts获取外部配置 const opts = state.opts; if (Object.keys(opts).length === 0) { return; } if (opts.replace && Object.keys(opts.replace).includes(path.node.name)) { // 替换节点 path.node.name = opts.replace[path.node.name]; } }
module.exports = function () { return { visitor: { Identifier: visitor, }, }; }; ``` 注意:babel的常用工具包已经集成在 babel-loader中 ,所以不需要再次安装了
参考:
https://babeljs.io/
https://astexplorer.net/
https://www.webpackjs.com/loaders/babel-loader/