自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

為啥套娃?聊聊 Babel、Jscodeshift 和阿里媽媽的 Gogocode

開發(fā) 前端
path 是節(jié)點之間的關(guān)系,每個 path關(guān)聯(lián)父節(jié)點和當(dāng)前節(jié)點,path 對象構(gòu)成一條從當(dāng)前節(jié)點到根結(jié)點的路徑。state 是遍歷過程中的共享數(shù)據(jù)的機制。

[[407446]]

首先,我是《babel 插件通關(guān)秘籍》 掘金小冊的作者,對 babel 有源碼級的掌握,算是有資格討論這個話題。

本來會探討以下話題:

  • babel 是怎么轉(zhuǎn)換代碼的
  • jscodeshift 是怎么轉(zhuǎn)換代碼的
  • babel 和 jscodeshift 的區(qū)別
  • 為什么不推薦 gogocode

babel 是怎么轉(zhuǎn)換代碼的

babel 編譯流程分為 3 步:parse、transform、generate

  • parse:把源碼轉(zhuǎn)成 AST,babel parser(babylon) 支持 esnext 語法,可通過插件支持 typescript、jsx、flow 等語法
  • transform:對 AST 進行轉(zhuǎn)換,通過 visitor 對不同的 AST 進行處理
  • generate:打印轉(zhuǎn)換后的 AST 為目標(biāo)代碼,并生成 sourcemap

轉(zhuǎn)換插件這樣寫(小冊中的一個 linter 的案例):

  1. const { declare } = require('@babel/helper-plugin-utils'); 
  2.  
  3. const noFuncAssignLint = declare((api, options, dirname) => { 
  4.     api.assertVersion(7); 
  5.  
  6.     return { 
  7.         pre(file) { 
  8.             file.set('errors', []); 
  9.         }, 
  10.         visitor: { 
  11.             AssignmentExpression(path, state) { 
  12.                 const errors = state.file.get('errors'); 
  13.                 const assignTarget = path.get('left').toString() 
  14.                 const binding = path.scope.getBinding(assignTarget); 
  15.                 if (binding) { 
  16.                     if (binding.path.isFunctionDeclaration() || binding.path.isFunctionExpression()) { 
  17.                         const tmp = Error.stackTraceLimit; 
  18.                         Error.stackTraceLimit = 0; 
  19.                         errors.push(path.buildCodeFrameError('can not reassign to function', Error)); 
  20.                         Error.stackTraceLimit = tmp; 
  21.                     } 
  22.                 } 
  23.             } 
  24.         }, 
  25.         post(file) { 
  26.             console.log(file.get('errors')); 
  27.         } 
  28.     } 
  29. }); 
  30.  
  31. module.exports = noFuncAssignLint; 

聲明 visitor 函數(shù),然后在遍歷的過程中會被調(diào)用,其中可以拿到 path 和 state 的 api:

path 是節(jié)點之間的關(guān)系,每個 path關(guān)聯(lián)父節(jié)點和當(dāng)前節(jié)點,path 對象構(gòu)成一條從當(dāng)前節(jié)點到根結(jié)點的路徑。state 是遍歷過程中的共享數(shù)據(jù)的機制。

通過 path 的一系列增刪改查 AST 的 api 來完成 transform。

比如下列 api:

  1. getSibling(key)  
  2. getNextSibling() 
  3. getPrevSibling() 
  4. getAllPrevSiblings() 
  5. getAllNextSiblings() 
  6. isXxx(opts) 
  7. assertXxx(opts) 
  8.  
  9. insertBefore(nodes) 
  10. insertAfter(nodes) 
  11. replaceWith(replacement) 
  12. replaceWithMultiple(nodes) 
  13. replaceWithSourceString(replacement) 
  14. remove() 

jscodeshift 是怎么轉(zhuǎn)換代碼的

jscodeshift 也是代碼轉(zhuǎn)換的工具,但是 api 風(fēng)格不同,是主動查找 AST,然后修改成新的 AST,最后生成代碼的形式:

  1. module.exports = function(fileInfo, api) { 
  2.   return api.jscodeshift(fileInfo.source) 
  3.     .findVariableDeclarators('foo'
  4.     .renameTo('bar'
  5.     .toSource(); 

jscodeshift 的優(yōu)勢是更簡潔。

但是 jscodeshift 能代替 babel 么?可以看下大牛給出的答案:

babel 和 jscodeshift 的不同

jscodeshift 的 parser 是 recast,曾經(jīng)有 babel 的維護者想結(jié)合這兩者:

https://github.com/facebook/jscodeshift/issues/168

利用 recast 做 parse,然后基于 babel parser 做轉(zhuǎn)換。

下面有一個很精彩的回復(fù),明確了 babel 和 jscodeshift 的不同:

我來梳理一下:

babel 的 transform api 是visitor 風(fēng)格,也就是聲明對什么 ast 做什么處理,然后在遍歷過程中被調(diào)用,這種不和具體遍歷方式耦合的寫法是一種設(shè)計模式(訪問者模式),處理再復(fù)雜的場景也能應(yīng)對。就是處理簡單場景顯得稍微啰嗦點。

jscodeshift 是 collection 風(fēng)格,類似 jquery,主動查找 ast,放到集合中操作,適合處理簡單場景,要知道每種 ast 是怎么查找到的,然后做轉(zhuǎn)換,要處理很多很多 case,萬一查找路徑不對,那可能就漏掉了一些情況,比起 babel 來,很難在復(fù)雜場景下沒有 bug。

就像 jquery 和 mvvm 的區(qū)別一樣,復(fù)雜場景還是 mvvm 的方式(babel)靠譜,不會漏掉一些 dom 沒處理。(只是一個類比)

所以,簡單場景可以用 jscodeshift,而所有場景都可以用 babel。

babel 的 visitor 的優(yōu)點就是設(shè)計模式中訪問者模式的優(yōu)點,不和具體遍歷方式耦合易于復(fù)用。

為什么不推薦 gogocode

gogocode 是這兩天阿里媽媽出的 ast 修改工具,基于 babel 做了一層封裝,說是簡化了 ast 操作。

api 類似這樣:

  1. $(code) 
  2.   .find('var a = 1'
  3.   .attr('declarations.0.id.name''c'
  4.   .root() 
  5.   .generate(); 

沒錯,基于 babel 的 visitor 風(fēng)格的 api 封裝出了 jscodeshift 的 collection 風(fēng)格的 api。

本來 babel 的 visitor 雖然寫起來麻煩一些,但是所有路徑都能夠處理到,而改成 collection 風(fēng)格之后,一旦落掉了某條路徑?jīng)]錯里,就會 bug。處理的 case 特別多,不適合復(fù)雜場景。

babel 本來的 visitor 模式是一種優(yōu)點,結(jié)果又在上層封裝出了 collection api。。如果想這么封裝,為啥不直接基于 jscodeshift 呢。。。我沒看懂這波操作。

我不看好這個 babel 套娃,我沒有自信保證復(fù)雜場景下能夠處理所有路徑而不遺漏 case,復(fù)雜場景我選擇直接用 babel 的 api。

總結(jié)

babel 是訪問者設(shè)計模式的實現(xiàn),分離了遍歷方式和對 AST 的操作,使得操作可以復(fù)用,jscodeshift 是 collection 風(fēng)格,類似 jquery,復(fù)雜場景容易落掉 case。

gogocode 基于babel 實現(xiàn)了 collection 風(fēng)格,不看好,容易落下 case。

一句話總結(jié):簡單場景可以用 jscodeshift,所有場景都可以用 babel,不怎么推薦 gogocode。

責(zé)任編輯:武曉燕 來源: 神光的編程秘籍
相關(guān)推薦

2021-08-31 06:51:18

Babel前端開發(fā)

2023-04-24 20:47:08

2023-08-29 22:41:02

2023-10-25 14:16:00

訓(xùn)練模型

2012-05-11 10:54:16

Qcon淘寶Fourinone

2015-05-13 19:30:21

2022-10-19 09:05:45

編譯程序員后端

2024-06-17 09:08:55

Linuxloop設(shè)備

2022-03-30 09:43:19

jscodeshif自動化重構(gòu)開發(fā)

2020-07-27 11:33:33

操作系統(tǒng)Windows游戲

2021-09-02 16:15:29

開發(fā)技能代碼

2023-08-01 09:10:46

阿里媽媽

2020-09-07 06:38:54

HA高可用協(xié)議

2021-09-11 19:46:14

配置

2021-09-02 13:38:48

Eslint Babel 插件

2021-09-17 06:28:20

JOIN阿里Java

2019-11-26 17:41:59

AI 數(shù)據(jù)人工智能

2015-10-23 17:35:23

LG

2017-06-16 15:16:32

點贊
收藏

51CTO技術(shù)棧公眾號