欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

 更新時(shí)間:2022年07月13日 17:03:17   作者:李李  
這篇文章主要介紹了vue?parseHTML函數(shù)源碼解析之析器遇到結(jié)束標(biāo)簽的示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

引言

承接上篇 parseHTML 函數(shù)源碼解析拿到返回值后的處理

接下來我們將會(huì)講解當(dāng) textEnd === 0 解析器遇到結(jié)束標(biāo)簽,parse 結(jié)束標(biāo)簽的代碼如下:

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

match函數(shù)匹配正則endTag

首先調(diào)用 html 字符串的 match 函數(shù)匹配正則 endTag ,將結(jié)果保存在常量endTagMatch中。正則 endTag 用來匹配結(jié)束標(biāo)簽,并且擁有一個(gè)捕獲組用來捕獲標(biāo)簽名字,比如有如下html 字符串:

<div></div>

endTagMatch 輸出如下:

endTagMatch = [
  '</div>',
  'div'
]

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

第二個(gè)元素是對(duì)應(yīng)的標(biāo)簽名字。

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

關(guān)鍵 parseEndTag 函數(shù)代碼

現(xià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);
		}
	}
}

你需要知道 parseEndTag 函數(shù)調(diào)用之前已經(jīng)獲得到了結(jié)束標(biāo)簽的名字以及結(jié)束標(biāo)簽在html(template)字符串中的起始和結(jié)束位置。 但是這并不代表著 html parser 結(jié)束了。

為什么?

還記得我們之前講的 stack 棧嗎? 之前我們講到通過stack可以檢測(cè)是否有非一元標(biāo)簽是否微寫閉合標(biāo)簽,接下來還會(huì)處理 stack 棧中剩余的標(biāo)簽。

除了這些功能之外,parseEndTag函數(shù)還會(huì)做一件事兒,如果你感興趣你可以在任何html文件中寫下如下內(nèi)容:

<body>
  </br>
  </p>
</body>

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

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

  • 檢測(cè)是否缺少閉合標(biāo)簽
  • 處理 stack 棧中剩余的標(biāo)簽
  • 解析</br> 與標(biāo)簽,與瀏覽器的行為相同

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

  • 第一種是處理普通的結(jié)束標(biāo)簽,此時(shí)三個(gè)參數(shù)都傳遞
  • 第二種是只傳遞第一個(gè)參數(shù)
  • 第三種是不傳遞參數(shù),處理 stack 棧剩余未處理的標(biāo)簽。

代碼并不復(fù)雜我們一起來看下吧!

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

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

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

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

其實(shí)這種使用 parseEndTag 函數(shù)的方式我們?cè)趆andleStartTag 函數(shù)中見過,當(dāng)時(shí)我們沒有對(duì)其進(jìn)行講解一起來回顧下。

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

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

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

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

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

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

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

 

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

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

接著,繼續(xù)解析該字符串,會(huì)遇到 <h2></h2> 標(biāo)簽并正常解析之,最后解析器會(huì)遇到一個(gè)單獨(dú)的p 標(biāo)簽的結(jié)束標(biāo)簽,即:</p>。

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

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

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

canBeLeftOpenTag(tagName) && lastTag === tagName

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

<p>max
<p>kaixin

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

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

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

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

最后更新 stack 棧以及 lastTag

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

了解下剩下的兩個(gè)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)
  }
}

這兩個(gè)else if 什么情況下成立呢?

  • 當(dāng) tagName 沒有在 stack 棧中找到對(duì)應(yīng)的開始標(biāo)簽時(shí),pos 為 -1 。
  • tagName為br 、p標(biāo)簽。

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

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

現(xiàn)在我們還剩一個(gè)問題沒有講解,即parseEndTag是如何處理stack棧中剩余未處理的標(biāo)簽的。其實(shí)就是調(diào)用 parseEndTag() 函數(shù)時(shí)不傳遞任何參數(shù),也就是說此時(shí) tagName 參數(shù)也不存在。這個(gè)時(shí)候我們?cè)俅尾榭聪旅娴拇a:

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

以上對(duì)于整個(gè)詞法分析的過程重點(diǎn)部分就已經(jīng)講解完畢了,其實(shí)現(xiàn)方式就是通過讀取字符流配合正則一點(diǎn)一點(diǎn)的解析字符串,直到整個(gè)字符串都被解析完畢為止。并且每當(dāng)遇到一個(gè)特定的token 時(shí)都會(huì)調(diào)用相應(yīng)的鉤子函數(shù),同時(shí)將有用的參數(shù)傳遞過去。比如每當(dāng)遇到一個(gè)開始標(biāo)簽都會(huì)調(diào)用 options.start 鉤子函數(shù),遇到閉合標(biāo)簽調(diào)用 options.end 鉤子函數(shù)。

下面我們來講講這兩個(gè)重要的鉤子函數(shù),并且談下AST的基本形成。

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

以上就是vue parseHTML函數(shù)解析器遇到結(jié)束標(biāo)簽的詳細(xì)內(nèi)容,更多關(guān)于vue parseHTML函數(shù)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Vue中的Strorage本地化存儲(chǔ)詳解

    Vue中的Strorage本地化存儲(chǔ)詳解

    這篇文章主要介紹了Vue中的Strorage本地化存儲(chǔ)詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-04-04
  • Vuex 入門教程

    Vuex 入門教程

    這篇文章主要介紹了Vuex 入門教程,Vuex 是一個(gè)專為 Vue.js 應(yīng)用程序開發(fā)的狀態(tài)管理模式, 那這個(gè) vuex 怎么用呢?就具體來看一下吧
    2018-01-01
  • Vue組件中使用防抖和節(jié)流實(shí)例分析

    Vue組件中使用防抖和節(jié)流實(shí)例分析

    在本篇文章里小編給大家整理的是一篇關(guān)于Vue組件中使用防抖和節(jié)流實(shí)例分析內(nèi)容,有需要的朋友們可以學(xué)習(xí)參考下。
    2021-11-11
  • 解決vue 打包發(fā)布去#和頁(yè)面空白的問題

    解決vue 打包發(fā)布去#和頁(yè)面空白的問題

    今天小編就為大家分享一篇解決vue 打包發(fā)布去#和頁(yè)面空白的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2018-09-09
  • vue的v-if里實(shí)現(xiàn)調(diào)用函數(shù)

    vue的v-if里實(shí)現(xiàn)調(diào)用函數(shù)

    這篇文章主要介紹了vue的v-if里實(shí)現(xiàn)調(diào)用函數(shù)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-07-07
  • vue中是怎樣監(jiān)聽數(shù)組變化的

    vue中是怎樣監(jiān)聽數(shù)組變化的

    這篇文章主要介紹了vue中是怎樣監(jiān)聽數(shù)組變化的,幫助大家更好的理解和學(xué)習(xí)vue,感興趣的朋友可以了解下
    2020-10-10
  • 前端vue中實(shí)現(xiàn)文件下載的幾種方法總結(jié)

    前端vue中實(shí)現(xiàn)文件下載的幾種方法總結(jié)

    這篇文章主要介紹了前端vue中實(shí)現(xiàn)文件下載的幾種方法總結(jié),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-04-04
  • vue如何使用外部特殊字體的操作

    vue如何使用外部特殊字體的操作

    這篇文章主要介紹了vue如何使用外部特殊字體的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2020-07-07
  • el-date-picker日期選擇限制范圍的實(shí)例代碼

    el-date-picker日期選擇限制范圍的實(shí)例代碼

    這篇文章主要介紹了el-date-picker日期選擇限制范圍,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-09-09
  • Vue支持搜索與篩選的用戶列表實(shí)現(xiàn)流程介紹

    Vue支持搜索與篩選的用戶列表實(shí)現(xiàn)流程介紹

    這篇文章主要介紹了Vue支持搜索與篩選的用戶列表實(shí)現(xiàn)流程,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧
    2022-10-10

最新評(píng)論