摘要:針對(duì)語法樹節(jié)點(diǎn)的查詢操作通常伴隨著和這兩種方法見下一篇文章。注意上述代碼打印出的和中的并不完全一致。如函數(shù),在中的為,但其實(shí)際的為。這個(gè)大家一定要注意哦,因?yàn)樵谖覀兒竺娴膶?shí)際代碼中也有用到。
??在上一篇文章中,我們介紹了AST的Create。在這篇文章中,我們接著來介紹AST的Retrieve。
??針對(duì)語法樹節(jié)點(diǎn)的查詢(Retrieve)操作通常伴隨著Update和Remove(這兩種方法見下一篇文章)。這里介紹兩種方式:直接訪問和traverse。
??本文中所有對(duì)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"] })
??對(duì)應(yīng)的AST explorer的表示如下圖所示,大家可以自行拷貝過去查看:
直接訪問??如上圖中,有很多節(jié)點(diǎn)Node,如需要獲取ExportDefaultDeclaration下的data函數(shù),直接訪問的方式如下:
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: [] } }
??這種直接訪問的方式可以用于固定程序結(jié)構(gòu)下的節(jié)點(diǎn)訪問,當(dāng)然也可以使用遍歷樹的方式來訪問每個(gè)Node。
??這里插播一個(gè)Update操作,把data函數(shù)修改為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的方式,在簡單場景下比較好用。可是對(duì)于一些復(fù)雜場景,存在以下幾個(gè)問題:
需要處理某一類型的Node,比如ThisExpression、ArrowFunctionExpression等,這時(shí)候我們可能需要多次遍歷AST才能完成操作
到達(dá)特定Node后,要訪問他的parent、sibling時(shí),不方便,但是這個(gè)也很常用
??@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引入了一個(gè)NodePath的概念,通過NodePath的API可以方便的訪問父子、兄弟節(jié)點(diǎn)。
??注意: 上述代碼打印出的node.type和AST explorer中的type并不完全一致。實(shí)際在使用traverse時(shí),需要以上述打印出的node.type為準(zhǔn)。如data函數(shù),在AST explorer中的type為Property,但其實(shí)際的type為ObjectMethod。這個(gè)大家一定要注意哦,因?yàn)樵谖覀兒竺娴膶?shí)際代碼中也有用到。
??仍以上述的訪問data函數(shù)為例,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的方法都可以,哪個(gè)更好一些,我也沒有研究。
??通過travase獲取到的是NodePath,NodePath.node等價(jià)于直接訪問獲取的Node節(jié)點(diǎn),可以進(jìn)行需要的操作。
??以下是一些從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節(jié)點(diǎn)找起
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中的時(shí)候比較有用
traverse(ast, { ObjectMethod(path) { if (path.node.key.name === "data") { const container = path.container console.log(container) // output: [...] } } })
getSibling 根據(jù)index獲取兄弟節(jié)點(diǎn)
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,執(zhí)行之后,就不會(huì)在對(duì)葉節(jié)點(diǎn)進(jìn)行遍歷
traverse(ast, { enter(path) { console.log(path.type) path.skip() } })
程序輸出根節(jié)點(diǎn)Program后結(jié)束。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/107869.html
摘要:操作通常配合來完成。因?yàn)槭莻€(gè)數(shù)組,因此,我們可以直接使用數(shù)組操作自我毀滅方法極為簡單,找到要?jiǎng)h除的,執(zhí)行就結(jié)束了。如上述代碼,我們要?jiǎng)h除屬性,代碼如下到目前為止,的我們都介紹完了,下面一篇文章以轉(zhuǎn)小程序?yàn)槔?,我們來?shí)戰(zhàn)一波。 ??通過前兩篇文章的介紹,大家已經(jīng)了解了Create和Retrieve,我們接著介紹Update和 Remove操作。Update操作通常配合Create來完成。...
摘要:生成屬性這一步,我們要先提取原函數(shù)中的的對(duì)象。所以這里我們還是主要使用來訪問節(jié)點(diǎn)獲取第一級(jí)的,也就是函數(shù)體將合并的寫法用生成生成生成插入到原函數(shù)下方刪除原函數(shù)程序輸出將中的屬性提升一級(jí)這里遍歷中的屬性沒有再采用,因?yàn)檫@里結(jié)構(gòu)是固定的。 ??經(jīng)過之前的三篇文章介紹,AST的CRUD都已經(jīng)完成。下面主要通過vue轉(zhuǎn)小程序過程中需要用到的部分關(guān)鍵技術(shù)來實(shí)戰(zhàn)。 下面的例子的核心代碼依然是最簡單...
摘要:序列化輸出定義序列化器字段的作用補(bǔ)充為列表,關(guān)系屬性字段的三種方式獲取序列化后的數(shù)據(jù),,反序列化輸入驗(yàn)證驗(yàn)證的調(diào)用接收數(shù)據(jù)繼承獲取查詢字符串中的某個(gè)參數(shù)值獲取查詢字符串中的所有值返回一個(gè)字典獲取請(qǐng)求體中的參數(shù)包括表單創(chuàng)建序列化器對(duì)象類字典進(jìn) 1.序列化輸出: 1.定義序列化器字段的作用 1. 2. 補(bǔ)充:read_only,write_only 2.`Json...
摘要:使用擴(kuò)展類使用了視圖集用于緩存返回列表數(shù)據(jù)的視圖,與擴(kuò)展類配合使用,實(shí)際是為方法添加了裝飾器用于緩存返回單一數(shù)據(jù)的視圖,與擴(kuò)展類配合使用,實(shí)際是為方法添加了裝飾器為視圖集同時(shí)補(bǔ)充和兩種緩存,與和一起配合使用。 在以往的后臺(tái)數(shù)據(jù)訪問時(shí),我們往往都會(huì)進(jìn)行數(shù)據(jù)庫查詢,基本的流程是這樣的: showImg(https://segmentfault.com/img/bVbooYc?w=784&h...
閱讀 3233·2021-11-23 09:51
閱讀 1040·2021-08-05 09:58
閱讀 671·2019-08-29 16:05
閱讀 981·2019-08-28 18:17
閱讀 3037·2019-08-26 14:06
閱讀 2730·2019-08-26 12:20
閱讀 2164·2019-08-26 12:18
閱讀 3072·2019-08-26 11:56