Vue AST的轉(zhuǎn)換實(shí)現(xiàn)方法講解
本節(jié),我們將討論關(guān)于AST的轉(zhuǎn)換。所謂AST的轉(zhuǎn)換,指的是對AST進(jìn)行一系列操作,將其轉(zhuǎn)換為新的AST的過程。新的AST可以是原語言或原DSL的描述,也可以是其他語言或其他DSL的描述。
例如,我們可以對模板AST進(jìn)行操作,將其轉(zhuǎn)換為JavaScriptAST。轉(zhuǎn)換后的AST可以用于代碼生成。這其實(shí)就是 Vue.js 的模板編譯器將模板編譯為渲染函數(shù)的過程
transform函數(shù)就是用來完成AST轉(zhuǎn)換工作的
為了對AST進(jìn)行轉(zhuǎn)換,我們需要能訪問AST 的每一個節(jié)點(diǎn),這樣才有機(jī)會對特定節(jié)點(diǎn)進(jìn)行修改、替換、刪除等操作。
由子AST 是樹型數(shù)據(jù)結(jié)構(gòu),所以我們需要編寫一個深度優(yōu)先的遍歷算法從而實(shí)現(xiàn)對AST中節(jié)點(diǎn)的訪回。
在開始編寫轉(zhuǎn)換代碼之前,需要一個dump工具函數(shù),用來打印當(dāng)前AST中節(jié)點(diǎn)的信息,如下面代碼所示:
function dump(node, indent = 0) { // 節(jié)點(diǎn)的類型 const type = node.type // 節(jié)點(diǎn)的描述,如果是根節(jié)點(diǎn),則沒有描述 // 如果是Element類型的節(jié)點(diǎn),則使用node.tag作為節(jié)點(diǎn)的描述 // 如果是Text類型的節(jié)點(diǎn),則使用node.content作為節(jié)點(diǎn)的描述 const desc = node.type === 'Root'?'':node.type === 'Element'?node.tag:node.content // 打印節(jié)點(diǎn)的類型和描述信息 console.log(`${'-'.repeat(indent)}${type}:${desc}`) // 遞歸地打印子節(jié)點(diǎn) if(node.children){ node.children.forEach(n=>demp(n,indent+2)) } }
dump 函數(shù)會輸出怎樣的結(jié)果:
const ast = parse(`<div><p>Vue</p><p>Template</p></div>`) console.log(dump(ast))
運(yùn)行上面這段代碼,將得到如下輸出:
Root:
--Element: div
----Element: p
------Text: Vue
----Element: p
-----Text: Template
接下來,我們將著手實(shí)現(xiàn)對AST 中節(jié)點(diǎn)的訪問。
代碼如下所示:
function traverseNode(ast){ const currentNode = ast const children = currentNode.children if(children){ for(let i=0,i<children.length;i++){ traverseNode(children[i]) } } }
traverseNode 函數(shù)用來以深度優(yōu)先的方式遍歷 AST,它的實(shí)現(xiàn)與 dump 幾乎相同
有traverseNode 函數(shù)之后,即可實(shí)現(xiàn)對 AST 中節(jié)點(diǎn)的訪問。例如,可以實(shí)現(xiàn)一個轉(zhuǎn)換功能,將AST中所有p標(biāo)簽轉(zhuǎn)換為h1標(biāo)簽,如下面的代碼所示:
function traverseNode(ast){ // 當(dāng)前節(jié)點(diǎn),ast本身就是Root節(jié)點(diǎn) const currentNode = ast // 對當(dāng)前節(jié)點(diǎn)進(jìn)行操作 if(currentNode.type === 'Element' && currentNode.tag === 'p'){ currentNode.tag = 'h1' } // 如果有子節(jié)點(diǎn),則遞歸地調(diào)用traverseNode函數(shù)進(jìn)行遍歷 // 遞歸調(diào)用 const children = currentNode.children if(children){ for(let i=0;i<children.length;i++){ traverseNode(children[i]) } } }
還可以對AST進(jìn)行其他轉(zhuǎn)換。例如,實(shí)現(xiàn)一個轉(zhuǎn)換,將文本節(jié)點(diǎn)的內(nèi)容重復(fù)兩次
function traverseNode(ast){ const currentNode = ast if(currentNode.type === 'Element' && currentNode.tag === 'p'){ currentNode.tag = 'h1' } if(currentNode.type === 'Text'){ currentNode.content = currentNode.content.repeat(2) } const children = currentNode.children if(children){ for(let i=0;i<children.length;i++){ traverseNode(children[i]) } } }
不過,隨著功能的不斷增加,traverseNode 函數(shù)將會變得越來越“臃腫”。這時,我們很自然地想到,能否對節(jié)點(diǎn)的操作和訪問進(jìn)行解耦呢? 答案是“當(dāng)然可以”,我們可以使用回調(diào)函數(shù)的機(jī)制來實(shí)現(xiàn)解耦,如下面代碼所示
// 接收第二個參數(shù)context function traverseNode(ast, context){ const currentNode = ast // context.nodeTransforms是一個數(shù)組,其中每一個元素都是一個函數(shù) const transforms = context.nodeTransforms for(let i = 0;i<transforms.length;i++){ // 將當(dāng)前節(jié)點(diǎn) currentNode 和 context 都傳遞給 nodeTransforms 中注冊的回調(diào)函數(shù) transforms[i](currentNode,context) } const children = currentNode.children if(children){ for(let i=0;i<children.length;i++){ traverseNode(children[i]) } } }
接著,我們把回調(diào)函數(shù)存儲到context.nodeTransforns 數(shù)組中,然后遍歷該數(shù)組,并逐個調(diào)用注冊在其中的回調(diào)函數(shù)。最后,我們將當(dāng)前節(jié)點(diǎn)curentNode和context對像分別作為參數(shù)傳遞給回調(diào)函數(shù)。
有了修改后的traverseNode函數(shù),就可以這樣使用它了
function transform(ast){ // 在transform函數(shù)內(nèi)創(chuàng)建context對象 const context = { //注冊 nodeTransforms 數(shù)組 nodeTransforms:[ transformElement, // transformElement 函數(shù)用來轉(zhuǎn)換標(biāo)簽節(jié)點(diǎn) transformText //transformText 函數(shù)用來轉(zhuǎn)換文本節(jié)點(diǎn) ] } //調(diào)用 traverseNode 完成轉(zhuǎn)換 traverseNode(ast, context) console.log(dump(ast)) }
其中transformElement和transformText函數(shù)的實(shí)現(xiàn)如下
function transformElement(node){ if(node.type === 'Element' && node.tag === 'p'){ node.tag = 'h1' } } function transformElement(node){ if(node.type === 'Text'){ node.content = node.content.repeat(2) } }
可以看到,解耦之后,節(jié)點(diǎn)操作封裝到了 transformELement和 transformText 這樣的獨(dú)立函數(shù)中。我們甚至可以編寫任意多個類似的轉(zhuǎn)換函數(shù),只需要將它們注冊到 context.nodeTransforns中即可。這樣就解決了功能增加所導(dǎo)致的 traverseNode 函數(shù)“臃腫”的問題。
到此這篇關(guān)于Vue AST的轉(zhuǎn)換實(shí)現(xiàn)方法講解的文章就介紹到這了,更多相關(guān)Vue AST轉(zhuǎn)換內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Vue 使用formData方式向后臺發(fā)送數(shù)據(jù)的實(shí)現(xiàn)
這篇文章主要介紹了Vue 使用formData方式向后臺發(fā)送數(shù)據(jù)的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04vue3導(dǎo)入excel并解析excel數(shù)據(jù)渲染到表格中(純前端實(shí)現(xiàn))
在Vue中實(shí)現(xiàn)導(dǎo)出Excel有多種方式,可以通過前端實(shí)現(xiàn),也可以通過前后端配合實(shí)現(xiàn),下面這篇文章主要給大家介紹了關(guān)于vue3導(dǎo)入excel并解析excel數(shù)據(jù)渲染到表格中的相關(guān)資料,文中介紹的方法是純前端實(shí)現(xiàn),需要的朋友可以參考下2024-04-04vue與vue-i18n結(jié)合實(shí)現(xiàn)后臺數(shù)據(jù)的多語言切換方法
下面小編就為大家分享一篇vue與vue-i18n結(jié)合實(shí)現(xiàn)后臺數(shù)據(jù)的多語言切換方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-03-03Vue2?Element?description組件列合并詳解
在使用Vue的時候經(jīng)常會涉及到表格的列合并,下面這篇文章主要給大家介紹了給大家Vue2?Element?description組件列合并的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-01-01