最近在做自动国际化,用到了babel
的插件, 所以为了减小自动国际化的篇幅,就把这部分单独提出来,目的是为了能让小白都能看懂babel
插件的真实面目。
babel插件
babel的编译流程:
主要分为三步,流程主要为:
parsing(解析)、transforming(转化)、generating(生成)
parsing:
它使用@babel/parser
库, 负责将es6
代码进行语法分析和词法分析后转换成抽象语法树AST
transforming:
plugin
插件利用@babel/traverse
库来,来遍历AST
节点,操作他们,用它提供的API
来编写对AST
的遍历和修改逻辑,由此来将一种AST
转换为另一种AST
generating:``@babel/generator
负责将处理后的AST树
生成新的 es5 代码
Plugins 和 Presets 的运行顺序
- Plugins 在 Presets 前运行
- Plugins 顺序从前往后排列
- Presets 顺序是颠倒的(从后往前)
推荐一个写插件好用的AST语法树结构工具,在写插件的时候,你不要知道他的type到底是什么,就可以用这个工具帮你查询。
AST抽象语法树结构网站: AST Explorer
babel编译文件
-
引入babel:
npm install --save-dev @babel/cli
-
在src/index.js文件里面写一个函数
js
const fn = () => {
console.log('hello babel')
}
3.用babel编译 执行命令
js
npx babel src --out-dir lib
结果
我想编译某一个文件就写这样:
js
npx babel src/index.js --out-dir lib
npx babel src/app.js --out-dir lib
在lib下面就会出现编译后的具体的文件。
babel插件实现
现在我们写一个插件,希望把具体index.js
里面 ceshi
的函数名改成test
,把你的函数copy
到 AST Explorer里面,然后点一下 ceshi
函数名,就会出现对应的 AST
结构。
其实babel
插件就是一个函数,把这个函数接入到babel
的配置文件中,它就会自动执行。所以在plugin/index.js
文件里面导出一个函数就好了。
在return里面导出一个编译器对象,它包含一个visitor
属性,它里面包含了我们要操作的内容,比如在上图,我们看到函数名的结构是:Identifier
, 那么咱们的插件就变成了这样的,顺便在控制台打印下path.name
看看
创建babel.config.js
文件,引入插件
执行命令:
js
npx babel src/index.js --out-dir lib
发现
写一下插件业务
js
module.exports = function () {
return {
visitor: {
Identifier(path) {
let name = path.node.name;
if (name === 'ceshi') {
console.log(path.node)
path.node.name = 'test'
}
},
},
};
}
执行npx babel src/index.js --out-dir lib
查看结果:
你看一个小小的babel插件就搞定了,我想打包发布到npm上,然后在其他包里面使用怎么办?
babel插件发布npm
npm login
输入用户名和密码,报错code 403
因为源地址应该是淘宝的,所以安装 npm 管理器:nrm,之前有个nvm是node版本管理器,现在出来个nrm,专门管理npm的源地址:
js
npm i nrm -g
nrm ls
nrm use npm
你要想看看现在用的是哪个镜像源,就执行下nrm ls
,或者 npm config get registry
现在再登陆
js
npm login
在发布之前,一定要创建一个文件是.npmignore
在这个文件里面写上你不需要发布的文件比如:
在发布之前,要对代码进行打包,在npm里面的文件一般都是处理以后的文件,这样publish的时候体积比较小,使用的时候才快。一般工具库打包都用的rollup,一般应用型项目才会用webpack和vite。
js
npm i rollup -D
创建配置文件:
在package.json
的scripts
里配置命令:
执行打包
js
npm run build
执行发布
js
npm publish
它会把你项目下面所有的东西都发布到npm上去,在发布过程中,我发现我的项目名字和别人的重复了,发布不上去,我就改了项目要名叫:babel-plugin-change-name1
备注:在打包的时候,请一定要将代码统一掉,要么全部是ESM 的,要么是commonjs的。我的项目采用的ESM,在package.json里面配置type:module就好了。当然 rollup 默认支持ESM,你要是想要打包commonJS的就要用插件了。所以没有必要这么做。我们只需要将工具包和使用包全部默认是ESM 就不会出现不必要的bug。
检查发现工具代码里面有commonJS的导出,所以赶紧改改,再次打包,发布即可。
测试babel插件
在另外一个项目里面安装测试下看看
js
npm i babel-plugin-change-name1 -D
执行babel编译命令报错
说明这个vite应用默认支持commonJS,需要在package.json里面配置
把babel插件里面的测试文件拿过来,然后再咱们的项目里面执行npx babel src/index.js --out-dir lib
查看结果:
终于把ceshi 改成了 test 是不是?
很明显这样不合理,我们一般都是执行打包的时候,babel才去执行的,现在是用一个命令在执行,我们想办法把他配到webpack或者vite里面去,当执行npm run build
的时候,babel
自动处理代码。
babel接入打包工具webpack、vite
vite集成babel
js
安装
pnpm i vite-plugin-babel @babel/core -D
- 然后进入
vite.config.ts
文件中使用babel插件,并且将build.target
设置为es2015
即可
js
import vue from '@vitejs/plugin-vue'
import babel from "vite-plugin-babel";
import vueJsx from '@vitejs/plugin-vue-jsx'
export default defineConfig({
base: './',
plugins: [
babel(),
vue(),
vueJsx(),
// 其他插件...
],
build: {
target: 'es2015'
}
})
webpack5 集成babel
安装
js
npm install -D babel-loader @babel/core @babel/preset-env
webpack.config.js
配置
js
module: {
rules: [
{
test: /\.?js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader'
}
}
]
}
babel.config.json
配置
js
{
"presets": [
["@babel/preset-env", {
"targets": "> 0.25%, not dead"
}]
],
"plugins": [
// 不污染全局,在运行时加载
["@babel/plugin-transform-runtime", {
"corejs": 3
}]
]
}
接入我们的项目
我们只需要引入babel-loader
就好了,具体babel的配置放到babel.config.js
里面,这样做的好处是webpack
的配置文件不会庞大而繁杂,难以阅读和修改。
执行打包
js
npm run dev
发现报错了,只要 package.json
文件里面写 type:module
那么所有的文件都要用 ESM
改完以后,打包正常
js
npm run build
此时你发现他成功了,但是你用index.html引入测试下看看
打开页面
因为打包的时候用了sourcemap,我们对应的是本地源码,他已经帮我们略过了中间环节,测试的时候,用npx babel src/index.js --out-dir lib
测试通过,就没有问题。
整个自定义babel插件流水线全部结束,希望您能和我一样有所收获。
总结:
-
插件名称必须是babel-plugin-xxx
-
.babelrc可以添加你的插件以及配置参数
-
插件编写必须遵循规范
-
当你想改变某个节点时,使用该节点名(首字母大写)称作为函数名进行访问,使用path.replaceWith或者repalceWithMultiple替换
参考文章:
# 前端工程化基石 -- AST(抽象语法树)以及AST的广泛应用🔥
babel-types: 官网
babel-handbook: Babel 手册
ast-explorer: AST 可视化
es-tree JavaScript 语法树规则