最近在做自动国际化,用到了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-envwebpack.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 语法树规则