国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

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

3403771864 / 456人閱讀

  在之前文章中我們講述了parseHTML 函數(shù)源碼解析拿到返回值后的處理,這篇文章就為我們講述了當 textEnd === 0 解析器遇到結(jié)束標簽,parse 結(jié)束標簽的代碼如下:

  // End tag:
  var endTagMatch = html.match(endTag);
  if (endTagMatch) {
  var curIndex = index;
  advance(endTagMatch[0].length);
  parseEndTag(endTagMatch[1], curIndex, index);
  continue
  }

  現(xiàn)在我們講講關(guān)于match函數(shù)匹配正則endTag

  首先調(diào)用 html 字符串的 match 函數(shù)匹配正則 endTag ,將結(jié)果保存在常量endTagMatch中。其實這樣是用來正則 endTag 用來匹配結(jié)束標簽,同時也可以用來捕獲標簽名字,比如有如下html 字符串:

  <div></div>

  endTagMatch 輸出如下:

  endTagMatch = [

  '</div>',

  'div'

  ]

  第一個元素是整個匹配到的結(jié)束標簽字符串

  第二個元素是對應(yīng)的標簽名字。

  首先假如匹配成功 if 語句塊的代碼將被執(zhí)行,則會首先使用 curIndex 常量存儲當前 index 的值,然后調(diào)用 advance 函數(shù),并以 endTagMatch[0].length 作為參數(shù),接著調(diào)用了 parseEndTag 函數(shù)對結(jié)束標簽進行解析,傳遞給 parseEndTag 函數(shù)的三個參數(shù)分別是:標簽名以及結(jié)束標簽在 html 字符串中起始和結(jié)束的位置,最后調(diào)用 continue 語句結(jié)束此次循環(huán)。

  關(guān)鍵 parseEndTag 函數(shù)代碼,我們先看看詳細具體代碼:  :

  function parseEndTag(tagName, start, end) {
  var pos, lowerCasedTagName;
  if (start == null) {
  start = index;
  }
  if (end == null) {
  end = index;
  }
  // Find the closest opened tag of the same type
  if (tagName) {
  lowerCasedTagName = tagName.toLowerCase();
  for (pos = stack.length - 1; pos >= 0; pos--) {
  if (stack[pos].lowerCasedTag === lowerCasedTagName) {
  break
  }
  }
  } else {
  // If no tag name is provided, clean shop
  pos = 0;
  }
  if (pos >= 0) {
  // Close all the open elements, up the stack
  for (var i = stack.length - 1; i >= pos; i--) {
  if (i > pos || !tagName &&
  options.warn
  ) {
  options.warn(
  ("tag <" + (stack[i].tag) + "> has no matching end tag.")
  );
  }
  if (options.end) {
  options.end(stack[i].tag, start, end);
  }
  }
  // Remove the open elements from the stack
  stack.length = pos;
  lastTag = pos && stack[pos - 1].tag;
  } else if (lowerCasedTagName === 'br') {
  if (options.start) {
  options.start(tagName, [], true, start, end);
  }
  } else if (lowerCasedTagName === 'p') {
  if (options.start) {
  options.start(tagName, [], false, start, end);
  }
  if (options.end) {
  options.end(tagName, start, end);
  }
  }
  }

  現(xiàn)在你你需要知道 parseEndTag 函數(shù)調(diào)用之前已經(jīng)獲得到了結(jié)束標簽的名字以及結(jié)束標簽在html(template)字符串中的起始和結(jié)束位置。

  為什么要這樣?

  在我們很早講 stack 棧,就有說過通過stack可以檢測是否有非一元標簽是否微寫閉合標簽,接下來還會處理 stack 棧中剩余的標簽。

  當然除此之外,我們的parseEndTag函數(shù)還會做一件事兒,下面是具體的內(nèi)容:

  <body>

  </br>

  </p>

  </body>

  上面的html片段中,我們分別寫了</br>、</p>的結(jié)束標簽,但注意我們并沒有寫起始標簽,然后瀏覽器是能夠正常解析他們的,其中 </br> 標簽被正常解析為 <br> 標簽,而</p>標簽被正常解析為 <p></p> 。除了 br 與 p 其他任何標簽如果你只寫了結(jié)束標簽那么瀏覽器都將會忽略。所以為了與瀏覽器的行為相同,parseEndTag 函數(shù)也需要專門處理br與p的結(jié)束標簽,即:</br> 和</p>。

  總結(jié)parseEndTag 函數(shù)作用

  檢測是否缺少閉合標簽

  處理 stack 棧中剩余的標簽

  解析</br> 與標簽,與瀏覽器的行為相同

  當一個函數(shù)擁有兩個及以上功能的時候,最常用的技巧就是通過參數(shù)進行控制,還記得jQuery中的Access 嗎?parseEndTag 函數(shù)接收三個參數(shù),這三個參數(shù)其實都是可選的,根據(jù)傳參的不同其功能也不同。

  第一種是處理普通的結(jié)束標簽,此時三個參數(shù)都傳遞

  第二種是只傳遞第一個參數(shù)

  第三種是不傳遞參數(shù),處理 stack 棧剩余未處理的標簽。

  代碼并不復雜我們一起來看下吧!

  var pos, lowerCasedTagName;
  if (start == null) {
  start = index;
  }
  if (end == null) {
  end = index;
  }

  定了兩個變量:pos和 lowerCasedTagName,其中變量 pos 會在后面用于判斷 html 字符串是否缺少結(jié)束標簽,lowerCasedTagName 變量用來存儲 tagName 的小寫版。

  接著是兩句if 語句,當 start 和 end 不存在時,將這兩個變量的值設(shè)置為當前字符流的讀入位置,即index。

  所以當我們看到這兩個 if 語句時,我們就應(yīng)該能夠想到:parseEndTag 函數(shù)的第二個參數(shù)和第三個參數(shù)都是可選的。

  其實這種使用 parseEndTag 函數(shù)的方式我們在handleStartTag 函數(shù)中見過,當時我們沒有對其進行講解一起來回顧下。

  if (expectHTML) {
  if (lastTag === 'p' && isNonPhrasingTag(tagName)) {
  parseEndTag(lastTag)
  }
  if (canBeLeftOpenTag(tagName) && lastTag === tagName) {
  parseEndTag(tagName)
  }
  }

  我們知道 lastTag 引用的是stack棧頂?shù)脑兀簿褪亲罱?或者說上一次)遇到的開始標簽,所以如下判斷條件:

  lastTag === 'p' && isNonPhrasingTag(tagName)

  這里想表達的意思是:最近一次遇到的開始標簽是 p 標簽,并且當前正在解析的開始標簽必須不能是段落式內(nèi)容(Phrasing content)模型,這時候 if 語句塊的代碼才會執(zhí)行,即調(diào)用parseEndTag(lastTag)。

  首先大家要知道每一個 html 元素都擁有一個或多個內(nèi)容模型(content model),其中p 標簽本身的內(nèi)容模型是流式內(nèi)容(Flow content),并且 p 標簽的特性是只允許包含段落式內(nèi)容(Phrasing content)。

  所以條件成立的情況如下:

  <p><h1></h1></p>

  在解析上面這段 html 字符串的時候,首先遇到p標簽的開始標簽,此時lastTag被設(shè)置為 p ,緊接著會遇到 h1 標簽的開始標簽,由于 h2 標簽的內(nèi)容模型屬于非段落式內(nèi)容(Phrasing content)模型,所以會立即調(diào)用 parseEndTag(lastTag) 函數(shù)閉合 p 標簽,此時由于強行插入了</p> 標簽,所以解析后的字符串將變?yōu)槿缦聝?nèi)容:

  <p></p><h2></h2></p>

  接著,繼續(xù)解析該字符串,會遇到 <h2></h2> 標簽并正常解析之,最后解析器會遇到一個多帶帶的p 標簽的結(jié)束標簽,即:</p>。

  這個時候就回到了我們前面講過的,當解析器遇到 p 標簽或者 br 標簽的結(jié)束標簽時會補全他們,最終<p><h2></h2></p> 這段 html 字符串將被解析為:

  <p></p><h2></h2><p></p>

  而這也就是瀏覽器的行為,以上是第一個if 分支的意義。還有第二個if分支,它的條件如下:

  canBeLeftOpenTag(tagName) && lastTag === tagName

  以上條件成立的意思是:當前正在解析的標簽是一個可以省略結(jié)束標簽的標簽,并且與上一次解析到的開始標簽相同,如下:

  <p>max

  <p>kaixin

  p 標簽是可以省略結(jié)束標簽的標簽,所以當解析到一個p標簽的開始標簽并且下一次遇到的標簽也是p標簽的開始標簽時,會立即關(guān)閉第二個p標簽。即調(diào)用:parseEndTag(tagName) 函數(shù),然后由于第一個p標簽缺少閉合標簽所以會Vue會給你一個警告。

  handleStartTag函數(shù)后續(xù)

  接下來我們繼續(xù)講解handleStartTag函數(shù)后續(xù)的內(nèi)容。

  if (tagName) {
  lowerCasedTagName = tagName.toLowerCase();
  for (pos = stack.length - 1; pos &gt;= 0; pos--) {
  if (stack[pos].lowerCasedTag === lowerCasedTagName) {
  break
  }
  }
  } else {
  // If no tag name is provided, clean shop
  pos = 0;
  }

  如果tagName存在,lowerCasedTagName 獲取的是 tagName 小寫之后的值,接下來開啟一個 for 循環(huán)從后向前遍歷 stack 棧,直到找到相應(yīng)的位置,并且該位置索引會保存到 pos 變量中,如果 tagName 不存在,則直接將 pos 設(shè)置為 0 。

  開頭我們講到 pos 變量會被用來判斷是否有元素缺少閉合標簽。怎么做到的呢?看完下面的代碼你就明白了。

  if (pos >= 0) {
  // Close all the open elements, up the stack
  for (var i = stack.length - 1; i >= pos; i--) {
  if (i > pos || !tagName &&
  options.warn
  ) {
  options.warn(
  ("tag <" + (stack[i].tag) + "> has no matching end tag.")
  );
  }
  if (options.end) {
  options.end(stack[i].tag, start, end);
  }
  }
  // Remove the open elements from the stack
  stack.length = pos;
  lastTag = pos && stack[pos - 1].tag;
  } else if (lowerCasedTagName === 'br') {
  if (options.start) {
  options.start(tagName, [], true, start, end);
  }
  } else if (lowerCasedTagName === 'p') {
  if (options.start) {
  options.start(tagName, [], false, start, end);
  }
  if (options.end) {
  options.end(tagName, start, end);
  }
  }

  上面代碼由三部分組成,即if...else if...else if。首先我們查看 if 語句塊,當 pos >= 0 的時候就會走 if 語句塊。在 if 語句塊內(nèi)開啟一個 for 循環(huán),同樣是從后向前遍歷 stack 數(shù)組,如果發(fā)現(xiàn) stack 數(shù)組中存在索引大于 pos 的元素,那么該元素一定是缺少閉合標簽的,這個時候如果是在非生產(chǎn)環(huán)境那么 Vue 便會打印一句警告,告訴你缺少閉合標簽。除了打印一句警告之外,隨后會調(diào)用 options.end(stack[i].tag, start, end) 立即將其閉合,這是為了保證解析結(jié)果的正確性。

  最后更新 stack 棧以及 lastTag


  stack.length = pos;
  lastTag = pos && stack[pos - 1].tag;

  了解下剩下的兩個else if:

 

 if (pos >= 0) {
  // ... 省略
  } else if (lowerCasedTagName === 'br') {
  if (options.start) {
  options.start(tagName, [], true, start, end)
  }
  } else if (lowerCasedTagName === 'p') {
  if (options.start) {
  options.start(tagName, [], false, start, end)
  }
  if (options.end) {
  options.end(tagName, start, end)
  }
  }

  這兩個else if 什么情況下成立呢?

  當 tagName 沒有在 stack 棧中找到對應(yīng)的開始標簽時,pos 為 -1 。

  tagName為br 、p標簽。

  當你寫了 br 標簽的結(jié)束標簽:</br> 或 p 標簽的結(jié)束標簽 </p> 時,解析器能夠正常解析他們,其中對于 </br> 會將其解析為正常的 <br> 標簽,而 </p> 標簽也會正常解析為<p></p>。

  可以發(fā)現(xiàn)對于 </br> 和 </p> 標簽瀏覽器可以將其正常解析為 <br> 以及<p></p>,Vue 的 parser 與瀏覽器的行為是一致的。

  到這里來說說最后一個問題,就是stack棧中剩余未處理的標簽的如何用parseEndTag來處理。簡單來說就是調(diào)用 parseEndTag() 函數(shù)時不傳遞任何參數(shù),也就是說此時 tagName 參數(shù)也不存在。看看具體代碼:

  由于 pos 為 0 ,所以 i >= pos 始終成立,這個時候 stack 棧中如果有剩余未處理的標簽,則會逐個警告缺少閉合標簽,并調(diào)用 options.end 將其閉合。

  上述內(nèi)容對于詞法分析說完,我們實現(xiàn)的方式就如同讀書一樣要一點點的理解解析字符串。其實在每當遇到一個特定的token 時都會調(diào)用相應(yīng)的鉤子函數(shù),同時將有用的參數(shù)傳遞過去。比如每當遇到一個開始標簽都會調(diào)用 options.start 鉤子函數(shù),遇到閉合標簽調(diào)用 options.end 鉤子函數(shù)。


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

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

相關(guān)文章

  • Vue.js 模板解析原理 - 來自《深入淺出Vue.js》第九章

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

    pinecone 評論0 收藏0
  • vue parseHTML函數(shù)源碼解析AST基本形成

      vue parseHTML函數(shù)解析器遇到結(jié)束標簽,在之前文章中已講述完畢。  例如有html(template)字符串:  <divid="app">   <p>{{message}}</p>   </div>  產(chǎn)出如下:  {   attrs:["id="app"","id...

    3403771864 評論0 收藏0
  • 講解vue parseHTML源碼解析harsendcomment鉤子函數(shù)

      我們現(xiàn)在要講述的是當解析器遇到一個文本節(jié)點時會如何為文本節(jié)點創(chuàng)建元素描述對象,那又該作何處理。  parseHTML(template,{   chars:function(){   //...   },   //...   })  chars源碼:   chars:functionchars(text){   if(!currentParent){   {   if(text===templ...

    3403771864 評論0 收藏0
  • Vue原理】Compile - 源碼版 之 Parse 主要流程

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

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

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

    huangjinnan 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<