ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
[TOC] >[success] # babel -- 初步了解 ~~~ 1.Babel 其实就是一个 JavaScript 的'编译器',但开箱即用的babel什么也不做。它只会把源文件复 制到构建目录去,整个过程也分为三个阶段解析(Parsing),转化(Transformation)以及 代码生成(Code Generation) 1.1.'解析'将源代码转换为一个更抽象的形式。 1.2.'转换'接受解析产生的抽象形式并且操纵这些抽象形式做任何编译器想让它们做的事。 1.3.'代码生成'基于转换后的代码表现形式(code representation)生成目标代码。 2.Babel 模块 source to source 的转换对应三个阶段依次 2.1.parse:通过 parser 把源码转成抽象语法树(AST)即词法语法解析生成ast一条龙 2.2.transform:遍历 AST,调用各种 transform 插件对 AST 进行增删改 2.3.generate:把转换后的 AST 打印成目标代码,并生成 sourcemap ~~~ * 依次对应babel 源码包位置 1. 解析 ——[@babel/parser](https://github.com/babel/babel/tree/master/packages/babel-parser)(包含 [tokenizer](https://github.com/babel/babel/tree/master/packages/babel-parser/src/tokenizer)、[parser](https://github.com/babel/babel/tree/master/packages/babel-parser/src/parser),即词法语法解析一体),根据 es2015, es2016… 标准,生成 AST 2. 转换 ——[@babel/traverse](https://github.com/babel/babel/tree/master/packages/babel-traverse)\+ plugins (visitor) 3. 代码生成 ——[@babel/generator](https://github.com/babel/babel/tree/master/packages/babel-generator) * [图片来自](https://mp.weixin.qq.com/s/LlQRx5SPmFgnTDO8VunGnw) ![](https://img.kancloud.cn/58/42/58420cd3b9ac250899c61201543954e5_668x249.png) >[info] ## 安装使用babel ~~~ 1.从 babel7 开始,所有的官方插件和主要模块,都放在了 @babel 的命名空间下。 从而可以避免在 npm 仓库中 babel 相关名称被抢注的问题,并且采用了Babel Monorepo 风格的仓库 2.在使用'babel' 时候我们一般需要安装'@babel/core' '@babel/cli' '@babel/preset-env' 2.1.'@babel/core' Babel 实现转换的核心,他是依赖能力更底层的 @babel/parser、 @babel/code-frame、@babel/generator、@babel/traverse、@babel/types等 2.1.1.'@babel/parser' 对源码进行 parse,可以通过 plugins、sourceType 等来指定 parse 语法, 例如指定是'ts',"jsx"语法,词法语法ast都在这个阶段 2.1.2.'@babel/code-frame'可以创建友好的报错信息 2.1.3.'@babel/traverse'通过 visitor 函数对遍历到的 ast 进行处理,分为 enter 和 exit 两个阶 段,具体操作 AST 使用 path 的 api,还可以通过 state 来在遍历过程中传递一些数据,简单 的说操作Ast 语法树 改变语法树结构自然达到了转换效果 2.1.4.'@babel/generator' 打印 AST 成目标代码字符串,支持 comments、minified、 sourceMaps 等选项。将语法树转换成对应新的编码 2.1.5.'@babel/types' 用于创建、判断 AST 节点,提供了 xxx、isXxx、assertXxx 的 api 2.2.'@babel/cli'--是 Babel 提供的命令行,它可以在终端中通过命令行方式运行,编译文件 或目录 2.3.'@babel/preset-env' -- Babel 只是一个'编译器' 你需要告诉他转换规则,需要在transformer 利用我们配置好的 plugins/presets把 Parser生成的 AST转变为新的 AST,即'@babel/preset-env' 就是一套转换规则集合 ~~~ >[danger] ##### 使用transform -- api ~~~ 1.'babel/core'中的'transform'方法转换传入的代码,转换完成后,执行callback,返回参数有 转换的代码,source map,和 AST,注意了 这里的'transform' 和之前提到的'transform'阶段不同 'transform' api 包含了阶段解析(Parsing),转化(Transformation)以及 代码生成(Code Generation)完整的三个阶段 ~~~ * 使用 ~~~ const core = require("@babel/core"); const code = "const a = 1;"; core.transform(code, {}, (err, result) => { console.log(result, err); // => { code, map, ast } // 输出转换结果没有变化 符合babel 只是一个编译器并不会做过多操作 console.log(result.code); // const a = 1; }); ~~~ * 配置转换规则 ~~~ const core = require("@babel/core"); const env = require("@babel/preset-env"); const code = "const a = 1;"; // 配置转换规则后 es6 =》 es5 core.transform(code, { presets: [env] }, (err, result) => { console.log(result, err); // => { code, map, ast } console.log(result.code); // '"use strict";\n\nvar a = 1;' }); ~~~ * 用更细致的api实现 ~~~ 1.'transform' api 其实是调用了"@babel/parser","@babel/generator","@babel/traverse",帮我们 完成了转换,如果不直接使用'transform' api 分步来做实际效果 2.针对之前'解析(Parsing)'、'转换(Transformation)'、'生成(Code Generation)' 三部曲依次对应 上面的包 2.1.'解析(Parsing)',@babel/parser,功能是把源码转成 AST 2.2.'转换(Transformation)','@babel/traverse',可以遍历 AST,并调用 visitor 函数修改 AST, '@babel/types' 包提供了对具体的 AST 节点的修改能力(AST的判断、创建、修改等) 2.3. '@babel/generator' 对新的 AST 进行聚合并生成 JavaScript 代码,同时生成 sourcemap 关于其他的包' @babel/code-frame',中途遇到错误想打印代码位置 ~~~ ~~~ const parser = require("@babel/parser"); const t = require("@babel/types"); const generate = require("@babel/generator").default; const traverse = require("@babel/traverse").default; const code = "const a = 1;"; // 功能是把源码转成 AST const ast = parser.parse(code); // 按照规则生成新的 AST traverse(ast, { VariableDeclaration(node, parent) { // console.log(node, parent) console.log(node.container[0].kind); node.container[0].kind = "var"; // const { node } = path // // // 获取函数名称等 // // path.replaceWith() // 替换为新的节点 // // path.remove() // 删除当前节点 // // path.skip() // 跳过子节点 // const newNode = t.variableDeclaration('const', [t.variableDeclarator('var')]) // path.replaceWith(newNode) // const copyNode = t.cloneNode(node) // 复制当前节点 // traverse(copyNode, {}, {}, path) // 对子树进行遍历和替换,不影响当前的path }, }); // 编译新的ast const output = generate(ast); console.log(output); // { code: 'var a = 1;', map: null, rawMappings: undefined } ~~~ >[info] ## 配置 ~~~ 1.在使用'babel/core'中的'transform'api 时候如果没有去配置规则,字符串在内部就是'一日游' 即code=> 转换'ast' =>没规则保持转换'ast'=>没改变的'ast'再次变回之前的'code' 2.整个配置中比较常用的两个字段 "presets"预设,"plugins" 插件,Babel 的'预设(preset)' 可以被看作是一组 Babel 'plugins'插件,更通俗的理解'plugins' 是一条条转换的规则,例如 来说转换成es6,'箭头函数'、'变量声明'等等这些都是规则,但是如果依次转换配置需要将 'es6'的配置都一一写入是一个很麻烦事,"presets"预设 将这中有统一规律的"plugins" 插件 装进一个预设中 { "presets": [], // 预设 "plugins": [] // 插件 } 3.Babel也是可以配置和其他工具具有类似的配置:ESLint(`.eslintrc`),Prettier(`.prettierrc`)。 'babel'配置文件的优先级 'babel.config.json < .babelrc < programmatic options from @babel/cli' ~~~ [更多配置](https://babel.docschina.org/docs/en/options/) >[danger] ##### plugins -- 插件 ~~~ 1.插件主要分为三类: 1.1.'syntax plugin':只是在parse阶段使用,可以让 parser 能够正确的解析对应的语法成 AST 1.2.'transform plugin':是对 AST 的转换,针对es20xx 中的语言特性、typescript、jsx 等的 转换都是在这部分实现的 1.3.'proposal plugin':未加入语言标准的特性的 AST 转换插件 ~~~ >[danger] ##### presets -- 预设 ~~~ 1.预设其实就是对于插件的一层封装,是一组插件的集合 2.不同类型的插件集合又产生了如下几种预设: 2.1.专门根据es标准处理语言特性的预设 -- babel-preset-es20xx 2.2.对其react、ts兼容的预设 -- preset-react preset-typescript 2.3目前最常使用的便是 @babel/preset-env这个预设 ~~~ >[danger] ##### 补充 [presets 和plugins 补充](https://zhuanlan.zhihu.com/p/164769597) >[danger] ##### 配置文件规则 ~~~ 1.根据官方文档的说法,目前有两类配置文件:项目范围配置 和 文件相对配置。 1.1.项目范围配置(全局配置) -- babel.config.json 1.2.文件相对配置(局部配置) -- .babelrc.json、package.json 2.区别:第一种配置作用整个项目,如果 babel 决定应用这个配置文件,则一定会应用到所 有文件的转换。而第二种配置文件只能应用到'当前目录'下的文件中。babel 在决定一个 js 文 件应用哪些配置文件时,会执行如下策略: 如果这个 js 文件在当前项目内,则会递归向上搜索 最近的一个 .babelrc 文件(直到遇到package.json),将其与全局配置合并。 ~~~ >[danger] ##### 总结 ~~~ 1.可能随着未来前端的发展出现越来越多的发展可能出现,如果一味的让某种工具必须直接具备 所有功能必然会出现臃肿代码 2.在工程化的角度上,需要秉承以下理念去设计一个工具链来解决上面问题 2.1.'可插拔(Pluggable)',比如 Babel 需要有一套灵活的插件机制,召集第三方开发者力量, 同时还需要方便接入各种工具; 2.2.'可调式(Debuggable)',比如 Babel 在编译过程中,要提供一套 Source Map,来帮助使用者在编译结 果和编译前源码之间建立映射关系,方便调试; 2.3.'基于协定(Compact)',实现灵活的配置方式,开发者在'尽量还原规范'和'更小的编译产出体积'之间, 找到平衡。 3.设想一个思路利用'Babel' js 编译器的功能可以将代码转换为ast 语法树,在配合各种可插拔的组件去对 其介入进行转换,随着未来新的场景的更新只需要的的插件出现配合'babel' 语法解析完成想要的可能 ~~~ ![](https://img.kancloud.cn/a8/7e/a87e4a695a603e57190f6ab15b670660_652x523.png) >[info] ## 参考 [07 | 梳理混乱的 Babel,不再被编译报错困扰](https://kaiwu.lagou.com/course/courseInfo.htm?courseId=584&sid=20-h5Url-0#/detail/pc?id=5912) [babel-从入门到上手 ](https://mp.weixin.qq.com/s/LlQRx5SPmFgnTDO8VunGnw)