相信目前常與 es6 代碼打交道的同學對 babel 應該不會陌生,在 es6 代碼被編譯轉化為 es5 代碼的過程中,babel 插件顯得尤為重要,我們最後經由 babel 生成的代碼取決于插件在這一層中做了什麼事,在探索這其中的過程之前,我們先來了解下一些所需的基礎知識。
在這其中,了解清楚 ast 十分重要,我們之是以需要将代碼轉換為 ast 也是為了讓計算機能夠更好地進行了解。我們可以來看看下面這段代碼被解析成 ast 後對應的結構圖:
在插件裡進行節點周遊需要先了解 visitor 和 path 的概念,前者相當于從衆多節點類型中選擇開發者所需要的節點,後者相當于對節點之間的關系的通路。
繼續上述所說的周遊,其實這種周遊會讓每個節點都會被通路兩次,一次是向下周遊代表進入(enter),一次是向上退出(exit)。是以實際上每個節點都會有 <code>enter</code> 和 <code>exit</code> 方法,在實際操作的時候需要注意這種周遊方式可能會引起的一些問題,上述例子是省略掉 <code>enter</code> 的簡寫。
visitor 模式中我們對節點的通路實際上是對節點路徑的通路,在這個模式中我們一般把 <code>path</code> 當作參數傳入節點選擇器中。<code>path</code> 表示兩個節點之間的連接配接,通過這個對象我們可以通路到節點、父節點以及進行一系列跟節點操作相關的方法(類似 dom 的操作)。
具備了 ast 相關知識和了解 visitor、path 後,就可以編寫一個簡單的 babel 插件了。我們要把上述的 <code>abs</code> 函數換成原生支援的 <code>math.abs</code> 來進行調用 。
首先我們先解析下 <code>abs(-8)</code> 的 ast 結構,直接從表達式語句(expressionstatement)開始:
最後我們需要對此次函數調用不符合的節點進行過濾,過濾掉名字不等于 abs 的函數調用,因為 babel 在周遊的過程是遞歸的,如果不過濾做限制的話,程式将會一直運作最終報調用棧超過門檻值的錯誤。
<code>rangeerror: unknown: maximum call stack size exceeded</code>
最終代碼如下:
上述例子使用了 <code>transform</code> api 直接解析轉換生成了新的代碼,另外在單獨編寫 babel 插件的時候,暴露的參數裡一般都含有常用的 <code>babel-types</code> 對象供使用。
<a href="https://github.com/babel/babylon/blob/master/ast/spec.md" target="_blank">babylon-spec</a>
<a href="https://github.com/babel/babel/tree/master/packages/babel-types" target="_blank">babel-types</a>
<a href="https://www.h5jun.com/post/babel-for-es6-and-beyond.html" target="_blank">babel-for-es6-and-beyond</a>
<a href="https://www.sitepoint.com/understanding-asts-building-babel-plugin/" target="_blank">understanding-asts-building-babel-plugin</a>
<a href="https://github.com/thejameskyle/babel-handbook/blob/master/translations/zh-hans/plugin-handbook.md#toc-visitors" target="_blank">babel-handbook</a>
本文作者:波本
轉載自:http://taobaofed.org/blog/2016/09/29/babel-plugins/