從零開始寫一個babel插件

背景

須要分析JS執行的效率,因此須要記錄JS的執行時間,經過執行時間分析JS執行效率。可是因爲aync/await使用的盛行,形成在記錄函數執行時間的過程當中,會記錄異步執行的時長,形成時間記錄不許確,沒法分析JS函數真正執行的時間。javascript

看一段代碼java

const asyncFn = ms => new Promise((resolve, reject) => {
  setTimeout(resolve, ms);
});

const record = {
  start: 0,
  time: 0,
};

const fn = async () => {
  record.start = performance.now();
  await asyncFn(100);
  record.time = performance.now() - record.start;
  console.log(record.time);
}

fn();
複製代碼

經過瀏覽器控制檯能夠看到輸出結果103.84499999781838,每次執行時間可能會不一樣,但應該都在這個值附近。能夠看出因爲aync/await函數的存在致使fn函數的計時不許確,把異步函數的執行時間也計算在內了git

在Web端異步函數執行時通常是在請求外部相關接口,此時並不會阻礙Web端執行其餘JS函數,不會形成JS阻塞致使頁面卡頓,因此這種異步函數的執行時間並不在咱們的統計範圍內,咱們只統計web中代碼的執行時間。github

修正方式web

通常咱們能夠經過在async/await函數上下分開計時的方式來解決這個問題,具體能夠看下段代碼:瀏覽器

const asyncFn = ms => new Promise((resolve, reject) => {
  setTimeout(resolve, ms);
});

const record = {
  start: 0,
  time: 0,
};

const fn = async () => {
  record.start = performance.now();
  record.time = performance.now() - record.start;
  await asyncFn(100);
  record.start = performance.now();
  record.time = performance.now() - record.start;
  console.log(record.time);
}

fn();
複製代碼

經過在瀏覽中運行,能夠在控制檯中看到結果,大概是0.010000003385357559,能夠看出確實能夠解決異步函數帶來的影響,可是須要在每一個涉及async/await的地方都要這樣手動處理,你不以爲這樣很麻煩嗎?babel

目標

使用babel AST的能力,經過分析代碼的AST,自動處理async/await的計時問題,避免人工輸入大量重複代碼,減小出錯的機會。markdown

工具

一個優秀的工具是通往目標的階梯,而babel當之無愧是這個階梯。app

可視化AST平臺

點擊跳轉,能夠很方便查看代碼AST結構,有助於理解代碼和AST結構之間的關係,事半功倍。異步

將上面兩段代碼複製進AST,咱們能夠看到上述代碼的AST結構:

對咱們來講,須要重點關注的就是type爲Program的部分,這裏面描述了代碼片斷的AST結構。

babel生成AST流程介紹

寫babel插件以前,須要對babel的運做週期有個清晰的認識,這樣咱們才能知道本身寫的插件何時起做用、達到的效果是什麼,寫插件的過程當中才能遊刃有餘。

Github上對babel的介紹點擊跳轉,能夠查看對babel插件內容的基本介紹,包括AST中各類節點的定義類型、遍歷方式、插件入口等,建議詳細查看一遍。因爲相關內容,該文檔已經比較詳細,編寫插件過程當中,方便進行各類API使用方法的查詢手冊,所以本文中就再也不贅述這部份內容了。

找處處理目標節點

因爲本次只是涉及async/await函數相關操做,咱們將關注type爲AwaitExpression類型的AST節點

babel插件編寫格式

按照babel插件的格式,咱們須要默認導出一個函數,該函數接收一個babel對象做爲參數,返回一個具備visitor屬性的對象,具體爲:

export default function (babel) {
    return {
        visitor: {
            AwaitExpression(path) {},
        },
    };
}
複製代碼

在visitor對象中,定義咱們須要處理的節點類型進行處理便可,具體邏輯能夠參考github上代碼

babel插件處理流程

具體流程能夠參考github上代碼,單元測試部分,主要分爲三個部分:

  • 解析代碼字符串生成AST,主要利用babylon
  • 遍歷AST,進行節點處理,主要利用babel-traverse,這裏爲了簡便生成須要插入代碼的AST,使用了babel-template這個工具,使用起來超級方便,不再用本身手動使用babel-types去手動構建所須要的AST了
  • 根據處理後的AST生成代碼,主要利用babel-generator

總結

這樣一個簡單的babel插件就完成了,固然要是真實環境進行使用的話,還須要考慮不少其餘邊界條件,這裏從總體上介紹babel的插件編寫模式,便於後續有類似需求時,能夠快速上手。

插件代碼示例

能夠在github上找到本文中的代碼示例,方便進行效果測試和新插件開發。

相關文章
相關標籤/搜索