gmfe / Think

观麦前端团队的官方博客
68 stars 3 forks source link

Babel 的一些分享 #15

Open elliothux opened 6 years ago

elliothux commented 6 years ago

Intro

之前研究框架的时候了解过一些前端编译流程, 目前不管是前端框架如 React、Vue 或是 JS 方言如 TypeScript、CoffeeScript 等基本都是靠 Babel 来实现到 JS 的编译。而且我们还能用 Babel 做到更多好玩的事, 比如: 改造 JS 语言, 设计自己的语法糖。

原理

Babel 是一个源码到源码的编译器,它在编译 JS 的过程中会将源码转换为对象形式的 AST 节点。 Babel 编译 JS 文件的步骤分为解析(parse),转换(transform),生成(generate)三个步骤。

解析步骤接收代码并输出 AST(Abstract syntax tree: 抽象语法树(参考:Abstract syntax tree)。这个步骤分为两个阶段:词法分析(Lexical Analysis)和语法分析(Syntactic Analysis)。

转换步骤接收 AST 并对其进行遍历,在此过程中对节点进行添加、更新及移除等操作。代码生成步骤深度优先遍历最终的 AST 转换成字符串形式的代码,同时还会创建源码映射(source maps)。

要达到我们的目标,我们需要在转换步骤操作 AST 并对其进行更改。 AST 在 Babel 中以 JS 对象的形式存在,因此我们需要遍历每一个 AST 节点。

在 Babel 及其他很多编译器中,都使用访问者模式来遍历 AST 节点(参考: Visitor pattern - Wikipedia)。当我们谈及遍历到一个 AST 节点时,实际上我们是在访问它,这时 Babel 将会调用该类型节点的 Handler。如,当访问到一个函数声明时(FunctionDeclaration),将会调用 FunctionDeclaration() 方法并将当前访问的节点作为参数传入该函数。我们需要做的工作就是编写对应访问者的 Handler 来处理相应的 AST 节点。

每个 AST 节点都是以对象的形式存在的,因此我们可以很方便的把它当做普通对象处理,如替换属性、遍历等等。除此之外,Babel 还提供了一系列的实用方法方便我们处理 AST 节点。如:path.traverse 方法可以很方便的递归遍历 AST 节点。

在处理 AST 节点的过程中,我们可能需要构建新的 AST 节点,因此我们需要借助 BabelTypes: 一个包含所有 AST 节点类型构造器的工厂函数。在我们的每一个 Handler 中都默认接受一个 Babel 对象作为参数,BabelTypes 就是该对象的一个属性,方便我们引用它。然后参考 BabelTypes 文档和 AST Explorer,就可以很轻松的构建一个新的 AST 节点。

更多的资料可以参考 Babel Handbook,很有帮助。

参考资料

Babel 学习资料: Babel HandBook Babel 中所有的 AST 节点类型的定义: BabelTypes 把 JS 语句转换为可视化的 AST Tree: AST Explorer

自己实现的一些 Babel Plugin

在 JSX 中使用 If 指令:babel-plugin-jsx-if-directive 在 JSX 中使用 For 指令:babel-plugin-jsx-for-directive 在 React 中使用双向数据绑定:babel-plugin-jsx-two-way-binding 把 Scala 语言的自动参数特性引入 JS:babel-plugin-auto-argument