摘要:針對語法樹節點的查詢操作通常伴隨著和這兩種方法見下一篇文章。注意上述代碼打印出的和中的并不完全一致。如函數,在中的為,但其實際的為。這個大家一定要注意哦,因為在我們后面的實際代碼中也有用到。
??在上一篇文章中,我們介紹了AST的Create。在這篇文章中,我們接著來介紹AST的Retrieve。
??針對語法樹節點的查詢(Retrieve)操作通常伴隨著Update和Remove(這兩種方法見下一篇文章)。這里介紹兩種方式:直接訪問和traverse。
??本文中所有對AST的操作均基于以下這一段代碼
const babylon = require("babylon") const t = require("@babel/types") const generate = require("@babel/generator").default const traverse = require("@babel/traverse").default const code = ` export default { data() { return { message: "hello vue", count: 0 } }, methods: { add() { ++this.count }, minus() { --this.count } } } ` const ast = babylon.parse(code, { sourceType: "module", plugins: ["flow"] })
??對應的AST explorer的表示如下圖所示,大家可以自行拷貝過去查看:
直接訪問??如上圖中,有很多節點Node,如需要獲取ExportDefaultDeclaration下的data函數,直接訪問的方式如下:
const dataProperty = ast.program.body[0].declaration.properties[0] console.log(dataProperty)
程序輸出:
Node { type: "ObjectMethod", start: 20, end: 94, loc: SourceLocation { start: Position { line: 3, column: 2 }, end: Position { line: 8, column: 3 } }, method: true, shorthand: false, computed: false, key: Node { type: "Identifier", start: 20, end: 24, loc: SourceLocation { start: [Object], end: [Object], identifierName: "data" }, name: "data" }, kind: "method", id: null, generator: false, expression: false, async: false, params: [], body: Node { type: "BlockStatement", start: 27, end: 94, loc: SourceLocation { start: [Object], end: [Object] }, body: [ [Object] ], directives: [] } }
??這種直接訪問的方式可以用于固定程序結構下的節點訪問,當然也可以使用遍歷樹的方式來訪問每個Node。
??這里插播一個Update操作,把data函數修改為mydata:
const dataProperty = ast.program.body[0].declaration.properties[0] dataProperty.key.name = "mydata" const output = generate(ast, {}, code) console.log(output.code)
程序輸出:
export default { mydata() { return { message: "hello vue", count: 0 }; }, methods: { add() { ++this.count; }, minus() { --this.count; } } };使用Traverse訪問
??使用直接訪問Node的方式,在簡單場景下比較好用。可是對于一些復雜場景,存在以下幾個問題:
需要處理某一類型的Node,比如ThisExpression、ArrowFunctionExpression等,這時候我們可能需要多次遍歷AST才能完成操作
到達特定Node后,要訪問他的parent、sibling時,不方便,但是這個也很常用
??@babel/traverse庫可以很好的解決這一問題。traverse的基本用法如下:
// 該代碼打印所有 node.type。 可以使用`path.type`,可以使用`path.node.type` let space = 0 traverse(ast, { enter(path) { console.log(new Array(space).fill(" ").join(""), ">", path.node.type) space += 2 }, exit(path) { space -= 2 // console.log(new Array(space).fill(" ").join(""), "<", path.type) } })
程序輸出:
> Program > ExportDefaultDeclaration > ObjectExpression > ObjectMethod > Identifier > BlockStatement > ReturnStatement > ObjectExpression > ObjectProperty > Identifier > StringLiteral > ObjectProperty > Identifier > NumericLiteral > ObjectProperty > Identifier > ObjectExpression > ObjectMethod > Identifier > BlockStatement > ExpressionStatement > UpdateExpression > MemberExpression > ThisExpression > Identifier > ObjectMethod > Identifier > BlockStatement > ExpressionStatement > UpdateExpression > MemberExpression > ThisExpression > Identifier
??traverse引入了一個NodePath的概念,通過NodePath的API可以方便的訪問父子、兄弟節點。
??注意: 上述代碼打印出的node.type和AST explorer中的type并不完全一致。實際在使用traverse時,需要以上述打印出的node.type為準。如data函數,在AST explorer中的type為Property,但其實際的type為ObjectMethod。這個大家一定要注意哦,因為在我們后面的實際代碼中也有用到。
??仍以上述的訪問data函數為例,traverse的寫法如下:
traverse(ast, { ObjectMethod(path) { // 1 if ( t.isIdentifier(path.node.key, { name: "data" }) ) { console.log(path.node) } // 2 if (path.node.key.name === "data") { console.log(path.node) } } })
??上面兩種判斷Node的方法都可以,哪個更好一些,我也沒有研究。
??通過travase獲取到的是NodePath,NodePath.node等價于直接訪問獲取的Node節點,可以進行需要的操作。
??以下是一些從babel-handbook中看到的NodePath的API,寫的一些測試代碼,大家可以參考看下,都是比較常用的:
parent
traverse(ast, { ObjectMethod(path) { if (path.node.key.name === "data") { const parent = path.parent console.log(parent.type) // output: ObjectExpression } } })
findParent
traverse(ast, { ObjectMethod(path) { if (path.node.key.name === "data") { const parent = path.findParent(p => p.isExportDefaultDeclaration()) console.log(parent.type) } } })
find 從Node節點找起
traverse(ast, { ObjectMethod(path) { if (path.node.key.name === "data") { const parent = path.find(p => p.isObjectMethod()) console.log(parent.type) // output: ObjectMethod } } })
container 沒太搞清楚,訪問的NodePath如果是在array中的時候比較有用
traverse(ast, { ObjectMethod(path) { if (path.node.key.name === "data") { const container = path.container console.log(container) // output: [...] } } })
getSibling 根據index獲取兄弟節點
traverse(ast, { ObjectMethod(path) { if (path.node.key.name === "data") { const sibling0 = path.getSibling(0) console.log(sibling0 === path) // true const sibling1 = path.getSibling(path.key + 1) console.log(sibling1.node.key.name) // methods } } })
skip 最后介紹一下skip,執行之后,就不會在對葉節點進行遍歷
traverse(ast, { enter(path) { console.log(path.type) path.skip() } })
程序輸出根節點Program后結束。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/107869.html
摘要:操作通常配合來完成。因為是個數組,因此,我們可以直接使用數組操作自我毀滅方法極為簡單,找到要刪除的,執行就結束了。如上述代碼,我們要刪除屬性,代碼如下到目前為止,的我們都介紹完了,下面一篇文章以轉小程序為例,我們來實戰一波。 ??通過前兩篇文章的介紹,大家已經了解了Create和Retrieve,我們接著介紹Update和 Remove操作。Update操作通常配合Create來完成。...
摘要:生成屬性這一步,我們要先提取原函數中的的對象。所以這里我們還是主要使用來訪問節點獲取第一級的,也就是函數體將合并的寫法用生成生成生成插入到原函數下方刪除原函數程序輸出將中的屬性提升一級這里遍歷中的屬性沒有再采用,因為這里結構是固定的。 ??經過之前的三篇文章介紹,AST的CRUD都已經完成。下面主要通過vue轉小程序過程中需要用到的部分關鍵技術來實戰。 下面的例子的核心代碼依然是最簡單...
摘要:使用擴展類使用了視圖集用于緩存返回列表數據的視圖,與擴展類配合使用,實際是為方法添加了裝飾器用于緩存返回單一數據的視圖,與擴展類配合使用,實際是為方法添加了裝飾器為視圖集同時補充和兩種緩存,與和一起配合使用。 在以往的后臺數據訪問時,我們往往都會進行數據庫查詢,基本的流程是這樣的: showImg(https://segmentfault.com/img/bVbooYc?w=784&h...
閱讀 3223·2021-11-23 09:51
閱讀 1030·2021-08-05 09:58
閱讀 663·2019-08-29 16:05
閱讀 971·2019-08-28 18:17
閱讀 3028·2019-08-26 14:06
閱讀 2721·2019-08-26 12:20
閱讀 2154·2019-08-26 12:18
閱讀 3064·2019-08-26 11:56