成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

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

3403771864 / 545人閱讀

  vue parseHTML函數(shù)解析器遇到結(jié)束標(biāo)簽,在之前文章中已講述完畢。

  例如有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: ""
  }

  上面就是寫明AST(抽象語法樹)??

  但答案是:No 這個并非是我們想要的AST,parse 階段最終成為的樹形態(tài)應(yīng)該是與如上html(template)字符串的結(jié)構(gòu)一一對應(yīng)的:

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

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

 {
  type: 1,
  tag: "div"
  }

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

  節(jié)點(diǎn)中都包含有一個一個父節(jié)點(diǎn)和若干子節(jié)點(diǎn),需要添加兩個對象屬性:parent 和 children ,分別用來表示當(dāng)前節(jié)點(diǎn)的父節(jié)點(diǎn)和它所包含的子節(jié)點(diǎn): 

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

  同時每個元素節(jié)點(diǎn)還可能包含很多屬性 (attributes),但每個節(jié)點(diǎn)任然要添加attrsList屬性,是為了用來存儲當(dāng)前節(jié)點(diǎn)所擁有的屬性:

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

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

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

  我們現(xiàn)在的說是就要建立一個能夠類似如上所示的一個能夠描述節(jié)點(diǎn)關(guān)系的對象樹,讓節(jié)點(diǎn)與節(jié)點(diǎn)之間通過 parent 和 children 建立聯(lián)系,這樣就可以實(shí)現(xiàn)每個節(jié)點(diǎn)的 type 屬性用來標(biāo)識該節(jié)點(diǎn)的類別。

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

  現(xiàn)在我們總結(jié)所學(xué) parseHTML 函數(shù),只是在生成 AST 中的一個重要環(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
  }

  接下來重點(diǎn)就來看看他們做了什么。parse函數(shù)返回root,其中root 所代表的就是整個模板解析過后的 AST,現(xiàn)在我們要用到另兩個重要的鉤子函數(shù):options.start 、options.end。

    下面進(jìn)入Vue在進(jìn)行模板編譯詞法分析階段調(diào)用了parse函數(shù),

  解析html

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

  <div></div>

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

 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)的描述對象,接著判斷root 是否存在,如果不存在則直接將 element 賦值給 root 。當(dāng)解析這段 html 字符串時首先會遇到 div 元素的開始標(biāo)簽,此時 start 鉤子函數(shù)將被調(diào)用,最終 root 變量將被設(shè)置為:

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

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

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

  代碼重新改造

  此時需要把代碼重新改造。

  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 字符串時首先會遇到 div 元素的開始標(biāo)簽,此時 start 鉤子函數(shù)被調(diào)用,root變量被設(shè)置為:

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

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

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

  接著解析 html (template)字符串

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

 

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

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

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

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

  注意這個代碼:

  if (!unary) currentParent = element

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

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

  解析div

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

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

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

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

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

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

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

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

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

  這個退回操作就能保證在解析p開始標(biāo)簽的時候,stack中存儲的是p標(biāo)簽父級元素的描述對象。

  接下來繼續(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
  }

  上述代碼主要是為實(shí)現(xiàn),在遇見非一元標(biāo)簽的結(jié)束標(biāo)簽時,這樣就會退回currentParent 變量的值為之前的值,這樣我們就修正了當(dāng)前正在解析的元素的父級元素。



文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/127819.html

相關(guān)文章

  • Vue原理】Compile - 源碼版 之 Parse 主要流程

    寫文章不容易,點(diǎn)個贊唄兄弟 專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究基于 Vue版本 【2.5.17】 如果你覺得排版難看,請點(diǎn)擊 下面鏈接 或者 拉到 下面關(guān)注公眾號也可以吧 【Vue原理】Compile - 源碼版 之 Parse 主要流程 本文難度較繁瑣,需要耐心觀看,如果你對 compile 源碼暫時...

    Forest10 評論0 收藏0
  • Vue源碼解析之Template轉(zhuǎn)化為AST

    摘要:下面用具體代碼進(jìn)行分析。匹配不到那么就是開始標(biāo)簽,調(diào)用函數(shù)解析。如這里的轉(zhuǎn)化為加上是為了的下一步轉(zhuǎn)為函數(shù),本文中暫時不會用到。再把轉(zhuǎn)化后的內(nèi)容進(jìn)。 什么是AST 在Vue的mount過程中,template會被編譯成AST語法樹,AST是指抽象語法樹(abstract syntax tree或者縮寫為AST),或者語法樹(syntax tree),是源代碼的抽象語法結(jié)構(gòu)的樹狀表現(xiàn)形式。...

    huangjinnan 評論0 收藏0
  • Vue編譯器AST抽象語法樹源碼分析

     直接進(jìn)入核心現(xiàn)在說說baseCompile核心代碼:  //`createCompilerCreator`allowscreatingcompilersthatusealternative   //parser/optimizer/codegen,e.gtheSSRoptimizingcompiler.   //Herewejustexportadefaultcompilerusingthede...

    3403771864 評論0 收藏0
  • vue parseHTML函數(shù)源碼解析AST預(yù)備知識

      在說Vue parse源碼之前,首先要了解周邊的工具函數(shù)。  之前見過element元素節(jié)點(diǎn)四描述對象?  varelement={   type:1,   tag:tag,   parent:null,   attrsList:attrs,   children:[]   }  是用一個createASTElement函數(shù),創(chuàng)建函數(shù)對象?! reateASTElement函數(shù)  funct...

    3403771864 評論0 收藏0
  • Vue.js 模板解析器原理 - 來自《深入淺出Vue.js》第九章

    摘要:模板解析器原理本文來自深入淺出模板編譯原理篇的第九章,主要講述了如何將模板解析成,這一章的內(nèi)容是全書最復(fù)雜且燒腦的章節(jié)。循環(huán)模板的偽代碼如下截取模板字符串并觸發(fā)鉤子函數(shù)為了方便理解,我們手動模擬解析器的解析過程。 Vue.js 模板解析器原理 本文來自《深入淺出Vue.js》模板編譯原理篇的第九章,主要講述了如何將模板解析成AST,這一章的內(nèi)容是全書最復(fù)雜且燒腦的章節(jié)。本文未經(jīng)排版,真...

    pinecone 評論0 收藏0

發(fā)表評論

0條評論

閱讀需要支付1元查看
<