從品牌網(wǎng)站建設(shè)到網(wǎng)絡(luò)營(yíng)銷(xiāo)策劃,從策略到執(zhí)行的一站式服務(wù)
來(lái)源:公司資訊 | 2021.08.19
1、簡(jiǎn)略描繪一下 Babel 的編譯進(jìn)程?
Babel 是一個(gè)源到源的轉(zhuǎn)化編譯器(Transpiler),它的首要作用是將 JavaScript 的高版別語(yǔ)法(例如 ES6)轉(zhuǎn)化成低版別語(yǔ)法(例如 ES5),然后可以適配瀏覽器的兼容性。
溫馨提示:假設(shè)某種高級(jí)言語(yǔ)或許運(yùn)用言語(yǔ)(例如用于人工智能的計(jì)算機(jī)規(guī)劃言語(yǔ))轉(zhuǎn)化的政策言語(yǔ)不是特定計(jì)算機(jī)的匯編言語(yǔ),而是面向另一種高級(jí)程序言語(yǔ)(許多研究性的編譯器將 C 作為政策言語(yǔ)),那么還需求將政策高級(jí)程序言語(yǔ)再進(jìn)行一次額外的編譯才干得到終究的政策程序,這種編譯器可稱(chēng)為源到源的轉(zhuǎn)化器。
從上圖可知,Babel 的編譯進(jìn)程首要可以分為三個(gè)階段:
解析(Parse):包括詞法分析和語(yǔ)法分析。詞法分析首要把字符流源代碼(Char Stream)轉(zhuǎn)化成令牌流( Token Stream),語(yǔ)法分析首要是將令牌流轉(zhuǎn)化成籠統(tǒng)語(yǔ)法樹(shù)(Abstract Syntax Tree,AST)。
轉(zhuǎn)化(Transform):經(jīng)過(guò) Babel 的插件才干,將高版別語(yǔ)法的 AST 轉(zhuǎn)化成支撐低版別語(yǔ)法的 AST。當(dāng)然在此進(jìn)程中也可以對(duì) AST 的 Node 節(jié)點(diǎn)進(jìn)行優(yōu)化操作,比如增加、更新以及移除節(jié)點(diǎn)等。
生成(Generate):將 AST 轉(zhuǎn)化成字符串形式的低版別代碼,一同也能創(chuàng)立 Source Map 映射。
具體的流程如下所示:
舉個(gè)栗子,假設(shè)要將 TypeScript 語(yǔ)法轉(zhuǎn)化成 ES5 語(yǔ)法:
// 源代碼
let a: string = 1;
// 政策代碼
var a = 1;
拷貝代碼
1
2
3
4
5
1.1 解析(Parser)
Babel 的解析進(jìn)程(源碼到 AST 的轉(zhuǎn)化)可以運(yùn)用 @babel/parser,它的首要特色如下:
支撐解析最新的 ES2020
支撐解析 JSX、Flow & TypeScript
支撐解析實(shí)驗(yàn)性的語(yǔ)法提案(支撐任何 Stage 0 的 PRS)
@babel/parser 首要是根據(jù)輸入的字符串流(源代碼)進(jìn)行解析,最終轉(zhuǎn)化成規(guī)范(根據(jù) ESTree 進(jìn)行調(diào)整)的 AST,如下所示:
import { parse } from '@babel/parser';
const source = `let a: string = 1;`;
enum ParseSourceTypeEnum {
Module = 'module',
Script = 'script',
Unambiguous = 'unambiguous',
}
enum ParsePluginEnum {
Flow = 'flow',
FlowComments = 'flowComments',
TypeScript = 'typescript',
Jsx = 'jsx',
V8intrinsic = 'v8intrinsic',
}
// 解析(Parser)階段
const ast = parse(source, {
// 嚴(yán)峻形式下解析而且答應(yīng)模塊定義
sourceType: ParseSourceTypeEnum.Module,
// 支撐解析 TypeScript 語(yǔ)法(留心,這兒只是支撐解析,并不是轉(zhuǎn)化 TypeScript)
plugins: [ParsePluginEnum.TypeScript],
});
需求留心,在 Parser 階段首要是進(jìn)行詞法和語(yǔ)法分析,假設(shè)詞法或許語(yǔ)法分析錯(cuò)誤,那么會(huì)在該階段被檢測(cè)出來(lái)。假設(shè)檢測(cè)正確,則可以進(jìn)入語(yǔ)法的轉(zhuǎn)化階段。
1.2 轉(zhuǎn)化(Transform)
Babel 的轉(zhuǎn)化進(jìn)程(AST 到 AST 的轉(zhuǎn)化)首要運(yùn)用 @babel/traverse,該庫(kù)包可以經(jīng)過(guò)訪(fǎng)問(wèn)者形式主動(dòng)遍歷并訪(fǎng)問(wèn) AST 樹(shù)的每一個(gè) Node 節(jié)點(diǎn)信息,然后完成節(jié)點(diǎn)的替換、移除和增加操作,如下所示:
import { parse } from '@babel/parser';
import traverse from '@babel/traverse';
enum ParseSourceTypeEnum {
Module = 'module',
Script = 'script',
Unambiguous = 'unambiguous',
}
enum ParsePluginEnum {
Flow = 'flow',
FlowComments = 'flowComments',
TypeScript = 'typescript',
Jsx = 'jsx',
V8intrinsic = 'v8intrinsic',
}
const source = `let a: string = 1;`;
// 解析(Parser)階段
const ast = parse(source, {
// 嚴(yán)峻形式下解析而且答應(yīng)模塊定義
sourceType: ParseSourceTypeEnum.Module,
// 支撐解析 TypeScript 語(yǔ)法(留心,這兒只是可以解析,并不是轉(zhuǎn)化 TypeScript)
plugins: [ParsePluginEnum.TypeScript],
});
// 轉(zhuǎn)化(Transform) 階段
traverse(ast, {
// 訪(fǎng)問(wèn)變量聲明標(biāo)識(shí)符
VariableDeclaration(path) {
// 將 const 和 let 轉(zhuǎn)化為 var
path.node.kind = 'var';
},
// 訪(fǎng)問(wèn) TypeScript 類(lèi)型聲明標(biāo)識(shí)符
TSTypeAnnotation(path) {
// 移除 TypeScript 的聲明類(lèi)型
path.remove();
},
});
關(guān)于 Babel 中的訪(fǎng)問(wèn)器 API,這兒不再過(guò)多說(shuō)明,假設(shè)想了解更多信息,可以檢查 Babel 插件手冊(cè)。除此之外,你可能現(xiàn)已留心到這兒的轉(zhuǎn)化邏輯其實(shí)可以理解為完成一個(gè)簡(jiǎn)略的 Babel 插件,只是沒(méi)有封裝成 Npm 包。當(dāng)然,在實(shí)在的插件開(kāi)發(fā)開(kāi)發(fā)中,還可以合作 @babel/types 工具包進(jìn)行節(jié)點(diǎn)信息的判別處理。
溫馨提示:這兒只是簡(jiǎn)略的一個(gè) Demo 示例,在實(shí)在轉(zhuǎn)化 let、const 等變量聲明的進(jìn)程中,還會(huì)遇到處理暫時(shí)性死區(qū)(Temporal Dead Zone, TDZ)的狀況,更多具體信息可以檢查官方的插件 babel-plugin-transform-block-scoping。