來源:公司資(zī)訊 | 2021.08.19
1、簡略描繪一(yī)下(xià) Babel 的編譯進程?
Babel 是一(yī)個源到源的轉化編譯器(Transpiler),它的首要作用是将 JavaScript 的高版别語法(例如 ES6)轉化成低版别語法(例如 ES5),然後可以适配浏覽器的兼容性。
溫馨提示:假設某種高級言語或許運用言語(例如用于人工(gōng)智能的計算機規劃言語)轉化的政策言語不是特定計算機的彙編言語,而是面向另一(yī)種高級程序言語(許多研究性的編譯器将 C 作爲政策言語),那麽還需求将政策高級程序言語再進行一(yī)次額外(wài)的編譯才幹得到終究的政策程序,這種編譯器可稱爲源到源的轉化器。
從上圖可知(zhī),Babel 的編譯進程首要可以分(fēn)爲三個階段:
解析(Parse):包括詞法分(fēn)析和語法分(fēn)析。詞法分(fēn)析首要把字符流源代碼(Char Stream)轉化成令牌流( Token Stream),語法分(fēn)析首要是将令牌流轉化成籠統語法樹(shù)(Abstract Syntax Tree,AST)。
轉化(Transform):經過 Babel 的插件才幹,将高版别語法的 AST 轉化成支撐低版别語法的 AST。當然在此進程中(zhōng)也可以對 AST 的 Node 節點進行優化操作,比如增加、更新以及移除節點等。
生(shēng)成(Generate):将 AST 轉化成字符串形式的低版别代碼,一(yī)同也能創立 Source Map 映射。
具體(tǐ)的流程如下(xià)所示:
舉個栗子,假設要将 TypeScript 語法轉化成 ES5 語法:
// 源代碼
let a: string = 1;
// 政策代碼
var a = 1;
拷貝代碼
1
2
3
4
5
1.1 解析(Parser)
Babel 的解析進程(源碼到 AST 的轉化)可以運用 @babel/parser,它的首要特色如下(xià):
支撐解析最新的 ES2020
支撐解析 JSX、Flow & TypeScript
支撐解析實驗性的語法提案(支撐任何 Stage 0 的 PRS)
@babel/parser 首要是根據輸入的字符串流(源代碼)進行解析,最終轉化成規範(根據 ESTree 進行調整)的 AST,如下(xià)所示:
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, {
// 嚴峻形式下(xià)解析而且答應模塊定義
sourceType: ParseSourceTypeEnum.Module,
// 支撐解析 TypeScript 語法(留心,這兒隻是支撐解析,并不是轉化 TypeScript)
plugins: [ParsePluginEnum.TypeScript],
});
需求留心,在 Parser 階段首要是進行詞法和語法分(fēn)析,假設詞法或許語法分(fēn)析錯誤,那麽會在該階段被檢測出來。假設檢測正确,則可以進入語法的轉化階段。
1.2 轉化(Transform)
Babel 的轉化進程(AST 到 AST 的轉化)首要運用 @babel/traverse,該庫包可以經過訪問者形式主動遍曆并訪問 AST 樹(shù)的每一(yī)個 Node 節點信息,然後完成節點的替換、移除和增加操作,如下(xià)所示:
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, {
// 嚴峻形式下(xià)解析而且答應模塊定義
sourceType: ParseSourceTypeEnum.Module,
// 支撐解析 TypeScript 語法(留心,這兒隻是可以解析,并不是轉化 TypeScript)
plugins: [ParsePluginEnum.TypeScript],
});
// 轉化(Transform) 階段
traverse(ast, {
// 訪問變量聲明标識符
VariableDeclaration(path) {
// 将 const 和 let 轉化爲 var
path.node.kind = 'var';
},
// 訪問 TypeScript 類型聲明标識符
TSTypeAnnotation(path) {
// 移除 TypeScript 的聲明類型
path.remove();
},
});
關于 Babel 中(zhōng)的訪問器 API,這兒不再過多說明,假設想了解更多信息,可以檢查 Babel 插件手冊。除此之外(wài),你可能現已留心到這兒的轉化邏輯其實可以理解爲完成一(yī)個簡略的 Babel 插件,隻是沒有封裝成 Npm 包。當然,在實在的插件開(kāi)發開(kāi)發中(zhōng),還可以合作 @babel/types 工(gōng)具包進行節點信息的判别處理。
溫馨提示:這兒隻是簡略的一(yī)個 Demo 示例,在實在轉化 let、const 等變量聲明的進程中(zhōng),還會遇到處理暫時性死區(Temporal Dead Zone, TDZ)的狀況,更多具體(tǐ)信息可以檢查官方的插件 babel-plugin-transform-block-scoping。