三分钟带你学会 自定义 Babel 插件

最近在做自动国际化,用到了babel的插件, 所以为了减小自动国际化的篇幅,就把这部分单独提出来,目的是为了能让小白都能看懂babel插件的真实面目。

babel插件

babel的编译流程:

主要分为三步,流程主要为:

parsing(解析)、transforming(转化)、generating(生成)

  1. parsing: 它使用@babel/parser库, 负责将es6代码进行语法分析和词法分析后转换成抽象语法树AST
  2. transforming: plugin插件利用 @babel/traverse库来,来遍历AST节点,操作他们,用它提供的 API 来编写对AST的遍历和修改逻辑,由此来将一种AST转换为另一种AST
  3. generating:``@babel/generator负责将处理后的 AST树 生成 新的 es5 代码

Plugins 和 Presets 的运行顺序

  • Plugins 在 Presets 前运行
  • Plugins 顺序从前往后排列
  • Presets 顺序是颠倒的(从后往前)

推荐一个写插件好用的AST语法树结构工具,在写插件的时候,你不要知道他的type到底是什么,就可以用这个工具帮你查询。

AST抽象语法树结构网站: AST Explorer

babel编译文件

  1. 引入babel:npm install --save-dev @babel/cli

  2. 在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,把你的函数copyAST 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
  1. 然后进入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替换

参考文章:

babel中文文档

# 前端工程化基石 -- AST(抽象语法树)以及AST的广泛应用🔥

babel-types: 官网

babel-handbook: Babel 手册

ast-explorer: AST 可视化

es-tree JavaScript 语法树规则

相关推荐
还是大剑师兰特39 分钟前
D3的竞品有哪些,D3的优势,D3和echarts的对比
前端·javascript·echarts
王解39 分钟前
【深度解析】CSS工程化全攻略(1)
前端·css
一只小白菜~1 小时前
web浏览器环境下使用window.open()打开PDF文件不是预览,而是下载文件?
前端·javascript·pdf·windowopen预览pdf
方才coding1 小时前
1小时构建Vue3知识体系之vue的生命周期函数
前端·javascript·vue.js
阿征学IT1 小时前
vue过滤器初步使用
前端·javascript·vue.js
王哲晓1 小时前
第四十五章 Vue之Vuex模块化创建(module)
前端·javascript·vue.js
丶21361 小时前
【WEB】深入理解 CORS(跨域资源共享):原理、配置与常见问题
前端·架构·web
发现你走远了1 小时前
『VUE』25. 组件事件与v-model(详细图文注释)
前端·javascript·vue.js
Mr.咕咕1 小时前
Django 搭建数据管理web——商品管理
前端·python·django
张张打怪兽1 小时前
css-50 Projects in 50 Days(3)
前端·css