babel知识体系漫谈
钉钉前端团队原创,点击右上角关注我们,了解更多前端技术
作者: 烛象
引言
在JavaScript蓬勃发展的今天,ES6/7、typescript已经成为代码编写的标配。
上一篇文章,我们介绍了钉钉IDL和自动生成typescript定义的工具,本文将会介绍AST相关js知识:babel。
关于babel
一句话阐述什么是babel:
babel是一个主要用于将ES2015+版本的代码编译成向下兼容(比如ES5/ES3)js版本的编译器。
// Babel Input: ES2015 arrow function [1, 2, 3].map((n) => n + 1); // Babel Output: ES5 equivalent [1, 2, 3].map(function(n) { return n + 1; }); 复制代码
结合实际使用场景,我们接触到的babel使用方式一般为
- .babelrc/babel.config.json (babel配置文件)
- babel-loader (webpack/rollup等)
然而,.babelrc的每一块配置后面究竟代表着babel怎样的处理方式,这个估计很少有人能讲得清楚。
babel知识体系
1、主要组件
- 源代码到AST: babel/parser(前身为babylon)
依赖acorn/acorn-jsx,用于将源码(如ES2015代码) 编译成 AST(抽象语法树)
- AST到输出代码: babel/generator
用于将AST转换为最终代码,根据不同的参数option,实现代码功能(比如sourceMap的实现)
2、结构转换
- 对AST实现颗粒化改造: @babel/traverse
通过AST节点遍历,方便使用方对AST节点进行逻辑重组。
import * as parser from "@babel/parser"; import traverse from "@babel/traverse"; // 源代码 const code = `function square(n) { return n * n; }`; const ast = parser.parse(code); traverse(ast, { enter(path) { // 节点转换 if (path.isIdentifier({ name: "n" })) { path.node.name = "x"; } } }); // 新代码 function square(x) {\n return x * x;\n} console.log(generate(ast)) 复制代码
3、工具组件
- 代码行列定位: @babel/code-frame
对代码行列进行定位
import { codeFrameColumns } from '@babel/code-frame'; const rawLines = `class Foo { constructor() }`; const location = { start: { line: 2, column: 16 } }; const result = codeFrameColumns(rawLines, location, { /* options */ }); console.log(result); // 结果如下 1 | class Foo { > 2 | constructor() | ^ 3 | } 复制代码
- 运行时优化(比如重复代码等): @babel/runtime(包含regenerator-runtime)
对代码重复率进行优化,比如优化如下class语法转换
class Circle {} function _classCallCheck(instance, Constructor) { //... } var Circle = function Circle() { _classCallCheck(this, Circle); }; 复制代码
通过@babel/runtime,将class语法以模块化的方式替换具体的实现,以达到减少重复代码的目的。
var _classCallCheck = require("@babel/runtime/helpers/classCallCheck"); var Circle = function Circle() { _classCallCheck(this, Circle); }; 复制代码
- 代码模板: @babel/template
模板代码,可以对比printf、Mustache的语法
import template from "@babel/template"; import generate from "@babel/generator"; import * as t from "@babel/types"; const buildRequire = template(` var %%importName%% = require(%%source%%); `); const ast = buildRequire({ importName: t.identifier("myModule"), source: t.stringLiteral("my-module"), }); // 最终的代码变为var myModule = require("my-module"); console.log(generate(ast).code); 复制代码
除了以上几个大件之外,babel工具体系还有types、helpers等来优化AST和generate最终代码。接下来要分享的是babel中非常重要的一环: plugins和presets。
4、babel plugins和 babel presets
babel作为编译器,它对代码的转换工作全部依赖于plugins(插件)。如果开发者连babelrc都没有配置的话,代码转换将什么都不做。
对于babel来说plugins的作用是转换代码。然而,js语法何其多,箭头函数、class语法、async/await等等,这么多语法需要非常庞大的插件体系。对于开发者来说,极其不友好。
babel在插件的概念基础上新增了个插件列表的概念,叫做presets(预设)。 比如@babel/preset-stage-0,代表的是支持stage-0语法的插件列表,通过presets解放了开发者极大的配置babel的工作量。
关于babel-polyfill
对于babel7.4.0,这个库官方已经废弃了,取而代之的是core-js/stable
和regenerator-runtime/runtime
。 对于很多babel-polyfill的使用方而言,这个库确实对bundle的最终大小产生了影响。官方推荐的是采用@babel/preset-env和useBuiltIns这个option配合起来,以便只引入你所需要的polyfill。
来看下这个能力有多酷炫:
var a = new Promise(); =转换为=> import "core-js/modules/es.promise"; var a = new Promise(); var b = new Map(); =转换为=> import "core-js/modules/es.map"; var b = new Map(); 复制代码
写在后面
关于babel更多的知识,欢迎大家移步babel官网。 希望读完本文你有一定的收获。
招人时间:
钉钉前端团队社招全面开启,求各路技术达人加入。
简历投递邮箱: xiaogang.hxg@alibaba-inc.com
笔者钉钉号: huangxiaogang
扫码关注更多钉钉技术
😊钉钉欢迎扫码关注《钉钉技术圈》
😊欢迎扫码关注《钉钉大前端团队》微信公众号
- JVM 知识体系漫谈
- 权限学习--BlueDavy之技术Blog漫谈权限系统之结尾篇(开源产品、个人观点、知识体系)
- 理想化的计算机科学知识体系
- 大型网站架构演变和知识体系
- 完整的大数据知识体系,大数据学习路线图
- 互联网用户研究必须具备的六大知识体系
- 项目管理9大知识体系与5个具体阶段 软考了
- Oracle知识体系(二)
- Python 编程核心知识体系
- Java全栈工程师知识体系介绍
- 架构师成长之路(4)--知识体系(方法)
- ACM数学知识体系
- java知识体系
- 如何快速全面建立自己的大数据知识体系?
- 构建知识体系,规划职业生涯
- 关于构建自己的知识体系架构的一点个人思考(转载)
- 大型网站架构演变和知识体系
- 软件工程师知识体系架构
- 大型网站架构演变和知识体系
- 物联网技术-需要学习的知识体系