共计 3139 个字符,预计需要花费 8 分钟才能阅读完成。
前段时间开始研究 ast,然后慢慢的顺便把 babel 都研究了,至于 ast 稍后的时间会写一篇介绍性博客专门介绍 ast,本博客先介绍一下 babel 的基本知识点。
背景:
由于现在前端出现了很多非 es5 的语法,如 jsx,.vue,ts 等等的格式和写法,如果要在浏览器的设备上识别并执行,需要额外将这些非传统格式的语法转成传统的 es5 格式,而 babel 插件,就是用来将非 es5 格式的语法转成 es5 语法。
babel 其实是一个解释器,它主要讲进行中的代码分为三个阶段执行:解释,转换,生成。其中 babel 插件或者其他插件都是在转换阶段起作用。
babel 核心包:
babel 既然是个解释器,那么就会拥有解释,遍历,以及生成的一系列工具和 api:
1)babylon:babel 里面用来将 js 代码词法分析,生成 ast,他的结构有些像 acron,它的返回的结构里面包含着 ast 和 tokens。
require(“babylon”).parse(“code”, {
// parse in strict mode and allow module declarations
sourceType: “module”,
plugins: [
// enable jsx and flow syntax
“jsx”,
“flow”
]
});
sourceType: module 表示的是在严格模式下解析并且允许模块定义(即能识别 import 和 expor 语法);script 识别不了。
2)babel-traverse:功能就像 estraverse 一样,主要是给 plugin 提供遍历 ast 节点的功能;
var babylon = require(‘babylon’);
var result = babylon.parse(code, { sourceType: “module”,});
console.log(‘result:’, result);
import traverse from “babel-traverse”;
traverse(result, {
enter(node) {
console.log(node);
}
});
3)babel-generator:将 ast 生成 js 代码;
var babylon = require(‘babylon’);
var result = babylon.parse(code, { sourceType: “module”,});
console.log(‘result:’, result);
import traverse from “babel-traverse”;
import generate from ‘babel-generator’;
traverse(result, {
enter(node) {
console.log(node);
}
});
var conde1 = generate(result);
console.log(‘generate:’, conde1);
babel 工具包:
要完成复杂的转换工作,单靠核心包是不能完成的,所以必要还要依赖于其他工具包辅助。
1)babel-types:包含着 ast 中的所有类型,可以生成一个 ast 的节点,然后替换真是 ast 的节点,从而改变 ast 的内容(ast 工具库,类似于 lodash,具有校验,创建和转换 ast 的方法)。
import * as t from “babel-types”;
console.log(t.stringLiteral(“my-module”));
语法:t.anyTypeAnnotation(内容) // 最终返回一个类型的对象
2)babel-template:可以通过字符串的形式生成一个 ast;
import template from “babel-template”;
const buildRequire = template(`
var IMPORT_NAME = require(SOURCE);
`);
const ast2 = buildRequire({
IMPORT_NAME: t.identifier(“myModule”),
SOURCE: t.stringLiteral(“my-module”)
});
console.log(‘ast2’, ast2);
3)babel-helps: 主要是用来协助 babel 转换;
4)babel-core-frame: 主要是用来将错误信息打印出来;
5)babel-cli:babel 的命令行工具,通过命令行对 js 代码进行转译;
6)babel-register: 因为 babel 工具文件,插件里面使用了很多 require,而 该文件可以将 node 中的 require 于 babel 中的 require 绑定,从而可以使用 require 引入文件;
7)babel-plugin-xxx: 在转换过程中使用的插件;
8)babel-plugin-transform-xxx: 在 transerform 过程中使用到的插件;
(.babelrc 文件:该文件会在 babel 编译过程中,自动配置 babel 的参数,babel 的运行环境 –env,babel 的设置 —preset,babel 的所需要用到的插件 —plugins 等)
9)babel-core:该核心包包含着 babel 的核心(babel-lon,babel-traverse,babel-generate),提供了更多更友善的 api 给开发者使用。
babel 编译原理:
编译器就是讲高级的语言或者语法,编译成更进阶机器识别的语言和语法;
babel 其实更像一个转译器,因为它主要是将高级的 js 语法转成低级的语法;
他们两者虽然有区别,但有很多相似之处(都是经历三个过程:解析,处理,生成);
以 es6 转成 es5 为例:
ES6 代码输入 ==》babylon 进行解析 ==》得到 AST ==》plugin 用 babel-traverse 对 AST 树进行遍历转译 ==》得到新的 AST 树 ==》用 babel-generator 通过 AST 树生成 ES5 代码
babel-pollfill,babel-runtime,transfer-runtime 的区别:
babel-pollfill 是针对于应用和页面范围内,对新的对象和新的语法进行兼容,主要是通过一些辅助函数进行兼容新的语法,但如果针对外部的库使用,就会产生污染全局环境的影响,一般对项目代码使用;
babel-runtime 是对于外部插件和库的语法兼容,能将新的对象和语法,通过在运行时,把对应的可识别的语法和对象匹配出来并进行转换,从而显示在运行时进行语法降级兼容,且不会产生全局污染,一般对外部的插件使用;
transfer-babel 是对 babel-runtime 进行封装,新的语法,对象能通过该插件,换种形式引用 runtime 的东西;
(其实 runtime,pollfill 都是建立在 core-js 之上的)。
对于 babel 的插件,主要是因为生成的 ast 的底层中有一个 accept 方法,专门用来接收 visitor(插件)访问者对象,然后在 visitor 中定义各种节点类型的操作 -visite,每个 visite 都可以接受一个 path 参数(节点信息,节点和位置信息的对象,其包含很多有用的方法),在 visit 中处理 path,从而实现转换的作用。
const result = babel.transform(code, {
plugins: [{
visitor
}]
})
console.log(result.code);
至于 visitor 后续会详细介绍。
整个 babel 的结构图,我大概花了一张图表示出来:
而 babel 和 webpack 的协同开发,我也大概花了一张图表示他们之间的关系,但里面的原理,我后续会再去研究,研究好再分享一下:
以上是我对 babel 的初步理解,如果有不正确的地方,欢迎指出。