babel转译器

657 阅读3分钟

babel 中文网址 www.babeljs.cn/docs/
github 用户手册
github 插件手册

1,babel定义

把es5+、jsx、ts等转为es5,以便浏览器运行

  • 语法转换
  • 通过polyfill 模块添加缺失的特性 (polyfill是shim的一种,shim是将不同 api封装成一种,比如 jQuery的 $.ajax封装了XMLHttpRequest和 IE用ActiveXObject方式创建xhr对象)
  • 源码转换(codemods)

2,babel配置

覆盖或增加默认的配置文件,与默认的配置merge

  • babel.config.json
    Babel 将自动在这个根目录中搜索一个babel.config.json文件

  • babelrc.json 从正在编译的“文件名”开始的目录结构搜索, 允许您为包的子部分创建独立的配置

  • 以babel:{} 形式添加到 package.json 文件中

内容格式

{ "presets": [...], "plugins": [...] }

presets:可以被看作是一组 Babel 插件,官方提供的预设如下:

ps: json文件后缀可扩展 .js.cjsand .mjs

3,工具包

一个简单demo:

import { parse } from '@babel/parser';
import traverse from '@babel/traverse';
import generate from '@babel/generator';

const code = 'const n = 1';

// parse the code -> ast
const ast = parse(code);

// transform the ast
traverse(ast, {
  enter(path) {
    // in this example change all the variable `n` to `x`
    if (path.isIdentifier({ name: 'n' })) {
      path.node.name = 'x';
    }
  },
});

// generate code <- ast
const output = generate(ast, code);
console.log(output.code); // 'const x = 1;'
  • babylon: babylon.parse(code, {})
  • @babel/parser: babelParser.parse(code, [options]),点击体验ast
  • @babel/traverse: 遍历转换nodes
  • @babel/generator: 将AST转成js代码
  • @babel/core: 一系列transform和parser函数,babel.transform(code: string, options?: Object, callback: Function)
  • @babel/template: 辅助函数,将字符串形式代码构建ast树
  • @babel/code-frame
  • @babel/runtime
  • @babel/types: 用于 AST 节点的 Lodash 式工具库

4,es6转es5

  • parser 解析 :解析位ast抽象语法树
  • transform 转化:通过配置的plugin和preset标准将ast转为新的ast
  • generator 生成:将转化后的ast生成新的code

5,自定义myPlugin

{ 
  "plugins": [
    ["babel-plugin-myPlugin",{ "key1": "value1", "method": "fn" }],
    "@babel/plugin-transform-runtime"
   ]
 } 
 
 
export default function({ types,template,generator,traverse,env }) { 
  return { 
  //用于在一个树状结构中获取具体节点的方法
   visitor: { 
      //每当在树中遇见一个 `Identifier` 的时候会调用
      Identifier(path) { 
         const name = path.node.name; 
         // 反转名称顺序
         path.node.name = name .split("") .reverse() .join(""); 
      },
      //var a = 1;
      VariableDeclaration(path) {},
      FunctionDeclaration(path) {},
      //
      ASTNodeTypeHere(path, state) {},
      //
      Program(path, state) {},
      //foo === bar
      BinaryExpression(path) {}
    }, 
  }; 
}

path属性如下,还包含添加、更新、移动和删除节点有关的其他很多方法

{
  "parent": {...},
  "node": {...},
  "hub": {...},
  "contexts": [],
  "data": {},
  "shouldSkip": false,
  "shouldStop": false,
  "removed": false,
  "state": null,
  "opts": null,
  "skipKeys": null,
  "parentPath": null,
  "context": null,
  "container": null,
  "listKey": null,
  "inList": false,
  "parentKey": null,
  "key": null,
  "scope": null,
  "type": null,
  "typeAnnotation": null
}
  • 替换
path.replaceWith(
    t.binaryExpression("**", path.node.left, t.numberLiteral(2))
  )
  • 插入
path.insertBefore   
path.get('body').pushContainer('body', t.expressionStatement(t.stringLiteral('after')))  
  • 删除
path.remove()
  • 替换父节点
path.parentPath.replaceWith
  • 提升变量声明至父级作用域
FunctionDeclaration(path) {
  const id = path.scope.generateUidIdentifierBasedOnNode(path.node.id);
  path.remove();
  path.scope.parent.push({ id, init: path.node });
}
  • 递归
path.traverse(MyVisitor)
  • 检查本地变量是否被绑定
if (path.scope.hasOwnBinding("n")) {...}

6、调试自定义plugin方式

我们自定义babel代码的位置为./plugin/index

6.1 通过babel命令

babel --plugin ./plugin/index src/demo.js --out-dir lib

6.2 @babel/core

const babel = require('@babel/core');
const myplugin = require('./plugin/index');
 
const code = `
  const sum = (a, b) => a + b;
`;
 
babel.transform(code, {
  plugins: [myplugin]
}).code;

写在后面

因为大学读的机械专业,最近才看了大学教程《编译原理》,真的有一种相见恨晚的感觉。觉得对于前端开发来说,这本书真的值得一读。目前读的还比较浅,只是对编译有了自己的认识而已,我最喜欢的一句话,“书读百遍,其义自现”,每次重读,都会有不一样的感受。