Genluo / Precipitation-and-Summary

梳理体系化的知识点&沉淀日常开发和学习
MIT License
16 stars 1 forks source link

babel相关资料 #95

Open Genluo opened 4 years ago

Genluo commented 4 years ago
Genluo commented 4 years ago

语法插件🤔

用babbel创造自定义JS语法 @babel/parser 已经支持了很多 JavaScript 语法特性,Parser也不支持扩展. 因此plugin-syntax-*实际上只是用于开启或者配置Parser的某个功能特性。

Genluo commented 4 years ago

辅助插件开发

  1. @babel/template: 某些场景直接操作AST太麻烦,就比如我们直接操作DOM一样,所以Babel实现了这么一个简单的模板引擎,可以将字符串代码转换为AST。比如在生成一些辅助代码(helper)时会用到这个库

  2. @babel/types: AST 节点构造器和断言. 插件开发时使用很频繁

  3. @babel/helper-*: 一些辅助器,用于辅助插件开发,例如简化AST操作

  4. @babel/helper: 辅助代码,单纯的语法转换可能无法让代码运行起来,比如低版本浏览器无法识别class关键字,这时候需要添加辅助代码,对class进行模拟。

Genluo commented 4 years ago

NodePath和Node的关联✅

副作用处理

修改了NodePath中对应的Node, 修改完成之后需要手动映射到NodePath变量中, path.replaceWith(path.node);

我们可以对 AST 进行任意的操作,比如删除父节点的兄弟节点、删除第一个子节点、新增兄弟节点... 当这些操作'污染'了 AST 树后,访问者需要记录这些状态,响应式(Reactive)更新 Path 对象的关联关系, 保证正确的遍历顺序,从而获得正确的转译结果。

作用域处理

如果修改了scope,也需要使用相关的path.scope来进行更新作用域信息

我们可以对 AST 进行任意的操作,比如删除父节点的兄弟节点、删除第一个子节点、新增兄弟节点... 当这些操作'污染'了 AST 树后,访问者需要记录这些状态,响应式(Reactive)更新 Path 对象的关联关系, 保证正确的遍历顺序,从而获得正确的转译结果。

Genluo commented 4 years ago

babel-macros

  1. issue
  2. awesome-babel-macros
  3. 开发文档

    提升语言的表现能力

    示例

    
    import evalm from 'eval.macro'
    const x = evalm`
    function fib(n) {
    const SQRT_FIVE = Math.sqrt(5);
    return Math.round(1/SQRT_FIVE * (Math.pow(0.5 + SQRT_FIVE/2, n) - Math.pow(0.5 - SQRT_FIVE/2, n)));
    }

fib(20) `

// ↓ ↓ ↓ ↓ ↓ ↓

const x = 6765



#### 使用Macro相比Plugin带来的好处

1. 很显然,Macro不需要配置.babelrc(当然babel-plugin-macros这个基座需要装好). 这个对于CRA这种不推荐配置构建脚本的工具来说很有帮助
2. 由隐式转换为了显式。上一节就说了“显式好于隐式”。你必须在源代码中通过导入语句声明你使用了 Macro; 而基于插件的方式,你可能不知道preval这个标识符哪里来的? 如何被应用?何时被应用?而且通常你还需要和其他工具链的配合,例如ESlint、Typescript声明等等。
3. Macro相比Plugin 更容易被实现。因为它专注于具体的 AST 节点,见下文
4. 另外,当配置出错时,Macro可以得到更好的错误提示
Genluo commented 4 years ago

节点静态求值

export function getJsxValue(path: NodePath<babelTypes.JSXAttribute>) {

  // generate
  // 单纯字符串
  if (babelTypes.isStringLiteral(path.node.value)) {
    return path.node.value.value;
  }
  // {} 这种类型
  if (babelTypes.isJSXExpressionContainer(path.node.value)) {
    const JSXExpressionContainerExpressionNode = path.node.value.expression;
    if (
      babelTypes.isStringLiteral(JSXExpressionContainerExpressionNode) ||
      babelTypes.isBooleanLiteral(JSXExpressionContainerExpressionNode) ||
      babelTypes.isNumericLiteral(JSXExpressionContainerExpressionNode)
    ) {
      return JSXExpressionContainerExpressionNode.value;
    }

    if (babelTypes.isObjectExpression(JSXExpressionContainerExpressionNode)) {
      const result = generate(JSXExpressionContainerExpressionNode);

      return eval(`(${result.code})`);
    }
  }
  if (path.node.value === null) {
    return true;
  }
  throw path.buildCodeFrameError("不能赋值为非基本类型");
}

通过内置api可以进行求值

t.evaluate(parse("5 + 5")) // { confident: true, value: 10 }
t.evaluate(parse("!true")) // { confident: true, value: false }
// ❌两个变量相加无法求值,因为变量值在运行时才存在,这里confident为false:  
t.evaluate(parse("foo + foo")) // { confident: false, value: undefined }