完整高頻題庫倉庫地址:https://github.com/hzfe/aweso...
完整高頻題庫閲讀地址:https://febook.hzfe.org/
相關問題
- Babel 是什麼
- Babel 有什麼用
- 壓縮代碼如何實現
回答關鍵點
JS 編譯器 AST 插件系統
Babel 是 JavaScript 編譯器:他能讓開發者在開發過程中,直接使用各類方言(如 TS、Flow、JSX)或新的語法特性,而不需要考慮運行環境,因為 Babel 可以做到按需轉換為低版本支持的代碼;Babel 內部原理是將 JS 代碼轉換為 AST,對 AST 應用各種插件進行處理,最終輸出編譯後的 JS 代碼。
知識點深入
1. AST 抽象語法樹
簡單定義:以樹的形式來表現編程語言的語法結構。
利用在線 playground 調試,可以對 AST 有個直觀感受:生成的樹有多個節點,節點有不同的類型,不同類型節點有不同的屬性。
const custom = "HZFE";
AST 是源代碼的高效表示,能便捷的表示大多數編程語言的結構。適用於做代碼分析或轉換等需求。之所以用樹來進行分析或轉換,是因為樹能使得程序中的每一節點恰好被訪問一次(前序或後續遍歷)。
常見使用場景:代碼壓縮混淆功能可以藉助 AST 來實現:分析 AST,基於各種規則進行優化(如 IF 語句優化;移除不可訪問代碼;移除 debugger 等),從而生成更小的 AST 樹,最終輸出精簡的代碼結果。
2. Babel 編譯流程
三大步驟
- 解析階段:Babel 默認使用 @babel/parser 將代碼轉換為 AST。解析一般分為兩個階段:詞法分析和語法分析。
- 詞法分析:對輸入的字符序列做標記化(tokenization)操作。
- 語法分析:處理標記與標記之間的關係,最終形成一顆完整的 AST 結構。
- 轉換階段:Babel 使用 @babel/traverse 提供的方法對 AST 進行深度優先遍歷,調用插件對關注節點的處理函數,按需對 AST 節點進行增刪改操作。
- 生成階段:Babel 默認使用 @babel/generator 將上一階段處理後的 AST 轉換為代碼字符串。
3. Babel 插件系統
Babel 的核心模塊 @babel/core,@babel/parser,@babel/traverse 和 @babel/generator 提供了完整的編譯流程。而具體的轉換邏輯需要插件來完成。
在使用 Babel 時,我們可通過配置文件指定 plugin 和 preset。而 preset 可以是 plugin 和 preset 以及其他配置的集合。Babel 會遞歸讀取 preset,最終獲取一個大的 plugins 數組,用於後續使用。
常見 presets
- @babel/preset-env
- @babel/preset-typescript
- @babel/preset-react
- @babel/preset-flow
最常見的 @babel/preset-env 預設,包含了一組最新瀏覽器已支持的 ES 語法特性,並且可以通過配置目標運行環境範圍,自動按需引入插件。
編寫 Babel 插件
Babel 插件的寫法是藉助訪問者模式(Visitor Pattern)對關注的節點定義處理函數。參考一個簡單 Babel 插件例子:
module.exports = function () {
return {
pre() {},
// 在 visitor 下掛載各種感興趣的節點類型的監聽方法
visitor: {
/**
* 對 Identify 類型的節點進行處理
* @param {NodePath} path
*/
Identifier(path) {
path.node.name = path.node.name.toUpperCase();
},
},
post() {},
};
};
使用該 Babel 插件的效果如下:
// input
// index.js
function hzfe() {}
// .babelrc
{
"plugins": ["babel-plugin-yourpluginname"]
}
``````
// output
function HZFE() {}
深入 Babel 轉換階段
在轉換階段,Babel 的相關方法會獲得一個插件數組變量,用於後續的操作。插件結構可參考以下接口。
interface Plugin {
key: string | undefined | null;
post: Function | void;
pre: Function | void;
visitor: Object;
parserOverride: Function | void;
generatorOverride: Function | void;
// ...
}
轉換階段,Babel 會按以下順序執行。詳細邏輯可查看源碼:
- 執行所有插件的 pre 方法。
- 按需執行 visitor 中的方法。
- 執行所有插件的 post 方法。
一般來説,寫 Babel 插件主要使用到的是 visitor 對象,這個 visitor 對象中會書寫對於關注的 AST 節點的處理邏輯。而上面執行順序中的第二步所指的 visitor 對象,是整合自各插件的 visitor,最終形成一個大的 visitor 對象,大致的數據結構可參考以下接口:
// 書寫插件時的 visitor 結構
interface VisitorInPlugin {
[ASTNodeTypeName: string]:
| Function
| {
enter?: Function;
exit?: Function;
};
}
// babel 最終整合的 visitor 結構
interface VisitorInTransform {
[ASTNodeTypeName: string]: {
// 不同插件對相同節點的處理會合併為數組
enter?: Function[];
exit?: Function[];
};
}
在對 AST 進行深度優先遍歷的過程中,會創建 TraversalContext 對象來把控對 NodePath 節點的訪問,訪問時調用對節點所定義的處理方法,從而實現按需執行 visitor 中的方法。詳細實現請看 babel-traverse 中的源碼。
參考資料
- AST
- Babel-handbook
- estree
- 訪問者模式