亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

vue?parseHTML?函數(shù)源碼解析AST基本形成

 更新時(shí)間:2022年07月13日 17:20:18   作者:李李  
這篇文章主要為大家介紹了vue?parseHTML?函數(shù)源碼解析AST基本形成,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

AST(抽象語(yǔ)法樹)?

vue parseHTML函數(shù)解析器遇到結(jié)束標(biāo)簽

在上篇文章中我們已經(jīng)把整個(gè)詞法分析的解析過(guò)程分析完畢了。

例如有html(template)字符串:

<div id="app">
  <p>{{ message }}</p>
</div>

產(chǎn)出如下:

{
attrs: [" id="app"", "id", "=", "app", undefined, undefined]
end: 14
start: 0
tagName: "div"
unarySlash: ""
}
{
attrs: []
end: 21
start: 18
tagName: "p"
unarySlash: ""
}

看到這不禁就有疑問(wèn)? 這難道就是AST(抽象語(yǔ)法樹)??

非常明確的告訴你答案:No 這不是我們想要的AST,parse 階段最終生成的這棵樹應(yīng)該是與如上html(template)字符串的結(jié)構(gòu)一一對(duì)應(yīng)的:

├── div
│   ├── p
│   │   ├── 文本

如果每一個(gè)節(jié)點(diǎn)我們都用一個(gè) javascript 對(duì)象來(lái)表示的話,那么 div 標(biāo)簽可以表示為如下對(duì)象:

{
  type: 1,
  tag: "div"
}

子節(jié)點(diǎn)

由于每個(gè)節(jié)點(diǎn)都存在一個(gè)父節(jié)點(diǎn)和若干子節(jié)點(diǎn),所以我們?yōu)槿缟蠈?duì)象添加兩個(gè)屬性:parent 和 children ,分別用來(lái)表示當(dāng)前節(jié)點(diǎn)的父節(jié)點(diǎn)和它所包含的子節(jié)點(diǎn):

{
  type: 1,
  tag:"div",
  parent: null,
  children: []
}

同時(shí)每個(gè)元素節(jié)點(diǎn)還可能包含很多屬性 (attributes),所以我們可以為每個(gè)節(jié)點(diǎn)添加attrsList屬性,用來(lái)存儲(chǔ)當(dāng)前節(jié)點(diǎn)所擁有的屬性:

{
  type: 1,
  tag:"div",
  parent: null,
  children: [],
  attrsList: []
}

按照以上思路去描述之前定義的 html 字符串,那么這棵抽象語(yǔ)法樹應(yīng)該長(zhǎng)成如下這個(gè)樣子:

{
  type: 1,
  tag: "div",
  parent: null,
  attrsList: [],
  children: [{
      type: 1,
      tag: "p",
      parent: div,
      attrsList: [],
      children:[
         {
          type: 3,
          tag:"",
          parent: p,
          attrsList: [],
          text:"{{ message }}"
         }
       ]
  }],
}

實(shí)際上構(gòu)建抽象語(yǔ)法樹的工作就是創(chuàng)建一個(gè)類似如上所示的一個(gè)能夠描述節(jié)點(diǎn)關(guān)系的對(duì)象樹,節(jié)點(diǎn)與節(jié)點(diǎn)之間通過(guò) parent 和 children 建立聯(lián)系,每個(gè)節(jié)點(diǎn)的 type 屬性用來(lái)標(biāo)識(shí)該節(jié)點(diǎn)的類別,比如 type 為 1 代表該節(jié)點(diǎn)為元素節(jié)點(diǎn),type 為 3 代表該節(jié)點(diǎn)為文本節(jié)點(diǎn)。

這里可參考NodeType:https://www.w3school.com.cn/jsref/prop_node_nodetype.asp

回顧我們所學(xué)的 parseHTML 函數(shù)可以看出,他只是在生成 AST 中的一個(gè)重要環(huán)節(jié)并不是全部。 那在Vue中是如何把html(template)字符串編譯解析成AST的呢?

Vue中是如何把html(template)字符串編譯解析成AST

在源碼中:

function parse (html) {
  var root;
  parseHTML(html, {
   start: function (tag, attrs, unary) {
      // 省略...
    },
    end: function (){
      // 省略...
    }
  }) 
  return root
}

可以看到Vue在進(jìn)行模板編譯詞法分析階段調(diào)用了parse函數(shù),parse函數(shù)返回root,其中root 所代表的就是整個(gè)模板解析過(guò)后的 AST,這中間還有兩個(gè)非常重要的鉤子函數(shù),之前我們沒(méi)有講到的,options.start 、options.end。

接下來(lái)重點(diǎn)就來(lái)看看他們做了什么。

解析html

假設(shè)解析的html字符串如下:

<div></div>

這是一個(gè)沒(méi)有任何子節(jié)點(diǎn)的div 標(biāo)簽。如果要解析它,我們來(lái)簡(jiǎn)單寫下代碼。

function parse (html) {
  var root;
  parseHTML(html, {
   start: function (tag, attrs, unary) {
      var element = {
        type: 1,
        tag: tag,
        parent: null,
        attrsList: attrs,
        children: []
      }
      if (!root) root = element
    },
    end: function (){
      // 省略...
    }
  }) 
  return root
}

如上: 在start 鉤子函數(shù)中首先定義了 element 變量,它就是元素節(jié)點(diǎn)的描述對(duì)象,接著判斷root 是否存在,如果不存在則直接將 element 賦值給 root 。當(dāng)解析這段 html 字符串時(shí)首先會(huì)遇到 div 元素的開始標(biāo)簽,此時(shí) start 鉤子函數(shù)將被調(diào)用,最終 root 變量將被設(shè)置為:

{
  type: 1,
  tag:"div",
  parent: null,
  children: [],
  attrsList: []
}

html 字符串復(fù)雜度升級(jí): 比之前的 div 標(biāo)簽多了一個(gè)子節(jié)點(diǎn),span 標(biāo)簽。

<div>
  <span></span>
</div>

代碼重新改造

此時(shí)需要把代碼重新改造。

function parse (html) {
  var root;
  var currentParent;
  parseHTML(html, {
   start: function (tag, attrs, unary) {
      var element = {
        type: 1,
        tag: tag,
        parent: null,
        attrsList: attrs,
        children: []
      }
      if (!root){
        root = element;
       }else if(currentParent){
        currentParent.children.push(element)
      }
      if (!unary) currentParent = element
    },
    end: function (){
      // 省略...
    }
  }) 
  return root
}

我們知道當(dāng)解析如上 html 字符串時(shí)首先會(huì)遇到 div 元素的開始標(biāo)簽,此時(shí) start 鉤子函數(shù)被調(diào)用,root變量被設(shè)置為:

{
  type: 1,
  tag:"div",
  parent: null,
  children: [],
  attrsList: []
}

還沒(méi)完可以看到在 start 鉤子函數(shù)的末尾有一個(gè) if 條件語(yǔ)句,當(dāng)一個(gè)元素為非一元標(biāo)簽時(shí),會(huì)設(shè)置 currentParent 為該元素的描述對(duì)象,所以此時(shí)currentParent也是:

{
  type: 1,
  tag:"div",
  parent: null,
  children: [],
  attrsList: []
}

接著解析 html (template)字符串

接著解析 html (template)字符串,會(huì)遇到 span 元素的開始標(biāo)簽,此時(shí)root已經(jīng)存在,currentParent 也存在,所以會(huì)將 span 元素的描述對(duì)象添加到 currentParent 的 children 數(shù)組中作為子節(jié)點(diǎn),所以最終生成的 root 描述對(duì)象為:

{
  type: 1,
  tag:"div",
  parent: null,
  attrsList: []
  children: [{
     type: 1,
     tag:"span",
     parent: div,
     attrsList: [],
     children:[]
  }], 
}

到目前為止好像沒(méi)有問(wèn)題,但是當(dāng)html(template)字符串復(fù)雜度在升級(jí),問(wèn)題就體現(xiàn)出來(lái)了。

<div>
 <span></span>
 <p></p>
</div>

在之前的基礎(chǔ)上 div 元素的子節(jié)點(diǎn)多了一個(gè) p 標(biāo)簽,到解析span標(biāo)簽的邏輯都是一樣的,但是解析 p 標(biāo)簽時(shí)候就有問(wèn)題了。

注意這個(gè)代碼:

if (!unary) currentParent = element

在解析 p 元素的開始標(biāo)簽時(shí),由于 currentParent 變量引用的是 span 元素的描述對(duì)象,所以p 元素的描述對(duì)象將被添加到 span 元素描述對(duì)象的 children 數(shù)組中,被誤認(rèn)為是 span 元素的子節(jié)點(diǎn)。而事實(shí)上 p 標(biāo)簽是 div 元素的子節(jié)點(diǎn),這就是問(wèn)題所在。

為了解決這個(gè)問(wèn)題,就需要我們額外設(shè)計(jì)一個(gè)回退的操作,這個(gè)回退的操作就在end鉤子函數(shù)里面實(shí)現(xiàn)。

解析div

這是一個(gè)什么思路呢?舉個(gè)例子在解析div 的開始標(biāo)簽時(shí):

stack = [{tag:"div"...}]

在解析span 的開始標(biāo)簽時(shí):

stack = [{tag:"div"...},{tag:"span"...}]

在解析span 的結(jié)束標(biāo)簽時(shí):

stack = [{tag:"div"...}]

在解析p 的開始標(biāo)簽時(shí):

stack = [{tag:"div"...},{tag:"p"...}]

在解析p 的標(biāo)簽時(shí):

這樣的一個(gè)回退操作看懂了嗎? 這就能保證在解析p開始標(biāo)簽的時(shí)候,stack中存儲(chǔ)的是p標(biāo)簽父級(jí)元素的描述對(duì)象。

接下來(lái)繼續(xù)改造我們的代碼。

function parse (html) {
  var root;
  var currentParent;
  var stack = [];  
  parseHTML(html, {
   start: function (tag, attrs, unary) {
      var element = {
        type: 1,
        tag: tag,
        parent: null,
        attrsList: attrs,
        children: []
      }
      if (!root){
        root = element;
       }else if(currentParent){
        currentParent.children.push(element)
      }
      if (!unary){
          currentParent = element;
          stack.push(currentParent);
       } 
    },
    end: function (){
      stack.pop();
      currentParent = stack[stack.length - 1]
    }
  }) 
  return root
}

通過(guò)上述代碼,每當(dāng)遇到一個(gè)非一元標(biāo)簽的結(jié)束標(biāo)簽時(shí),都會(huì)回退 currentParent 變量的值為之前的值,這樣我們就修正了當(dāng)前正在解析的元素的父級(jí)元素。

以上就是根據(jù) parseHTML 函數(shù)生成 AST 的基本方式,但實(shí)際上還不完美在Vue中還會(huì)去處理一元標(biāo)簽,文本節(jié)點(diǎn)和注釋節(jié)點(diǎn)等等。

接下來(lái)你是否迫不及待要進(jìn)入到源碼部分去看看了? 但Vue這塊代碼稍微復(fù)雜點(diǎn),我們還需要有一些前期的預(yù)備知識(shí)。

parseHTML 函數(shù)源碼解析 AST 預(yù)備知識(shí)

以上就是vue parseHTML 函數(shù)源碼解析AST基本形成的詳細(xì)內(nèi)容,更多關(guān)于vue parseHTML 函數(shù)AST的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • vue3中的watch和watchEffect實(shí)例詳解

    vue3中的watch和watchEffect實(shí)例詳解

    watch和watchEffect都是監(jiān)聽(tīng)器,但在寫法和使用上有所區(qū)別,下面這篇文章主要給大家介紹了關(guān)于vue3中watch和watchEffect的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-05-05
  • vue3手動(dòng)封裝彈出框組件message的方法

    vue3手動(dòng)封裝彈出框組件message的方法

    這篇文章主要為大家詳細(xì)介紹了vue3手動(dòng)封裝彈出框組件message的方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • VUE3安裝element?ui失敗的原因以及解決辦法

    VUE3安裝element?ui失敗的原因以及解決辦法

    這篇文章主要給大家介紹了關(guān)于VUE3安裝element?ui失敗的原因以及解決的相關(guān)資料,很多朋友升級(jí)使用Vue3了,但在安裝element?ui失敗出錯(cuò)了,這里給大家總結(jié)下,需要的朋友可以參考下
    2023-09-09
  • element-ui實(shí)現(xiàn)表格邊框的動(dòng)態(tài)切換并防抖

    element-ui實(shí)現(xiàn)表格邊框的動(dòng)態(tài)切換并防抖

    這篇文章主要介紹了element-ui實(shí)現(xiàn)表格邊框的動(dòng)態(tài)切換并防抖方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • vue3 使用socket的完整代碼

    vue3 使用socket的完整代碼

    這篇文章主要介紹了vue3 使用socket的完整代碼,包括vue3客戶端和服務(wù)端的實(shí)例講解,本文給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧
    2024-03-03
  • vue?實(shí)現(xiàn)手動(dòng)分割日期

    vue?實(shí)現(xiàn)手動(dòng)分割日期

    這篇文章主要介紹了vue?實(shí)現(xiàn)手動(dòng)分割日期,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-10-10
  • vue實(shí)現(xiàn)翻牌動(dòng)畫

    vue實(shí)現(xiàn)翻牌動(dòng)畫

    這篇文章主要為大家詳細(xì)介紹了vue實(shí)現(xiàn)翻牌動(dòng)畫,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-04-04
  • vue修改Element的el-table樣式的4種方法

    vue修改Element的el-table樣式的4種方法

    這篇文章主要介紹了vue修改Element的el-table樣式的4種方法,幫助大家更好的理解和使用vue,感興趣的朋友可以了解下
    2020-09-09
  • vue實(shí)現(xiàn)簡(jiǎn)單無(wú)縫滾動(dòng)效果

    vue實(shí)現(xiàn)簡(jiǎn)單無(wú)縫滾動(dòng)效果

    這篇文章主要為大家詳細(xì)介紹了vue實(shí)現(xiàn)簡(jiǎn)單無(wú)縫滾動(dòng)效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-04-04
  • ant design vue 清空upload組件圖片緩存的問(wèn)題

    ant design vue 清空upload組件圖片緩存的問(wèn)題

    這篇文章主要介紹了ant design vue 清空upload組件圖片緩存的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-10-10

最新評(píng)論