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

