最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

vue?parseHTML源碼解析hars?end?comment鉤子函數(shù)

 更新時間:2022年07月13日 17:43:28   作者:李李  
這篇文章主要為大家介紹了vue?parseHTML源碼解析hars?end?comment鉤子函數(shù)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

引言

接上文  parseHTML 函數(shù)源碼解析  start鉤子函數(shù)

接下來我們主要講解當解析器遇到一個文本節(jié)點時會如何為文本節(jié)點創(chuàng)建元素描述對象,又會如何對文本節(jié)點做哪些特殊的處理。

parseHTML(template, {
	chars: function(){
		//...
	},
	//...
})

chars源碼:

chars: function chars(text) {
	if (!currentParent) {
		{
			if (text === template) {
				warnOnce(
					'Component template requires a root element, rather than just text.'
				);
			} else if ((text = text.trim())) {
				warnOnce(
					("text \"" + text + "\" outside root element will be ignored.")
				);
			}
		}
		return
	}
	// IE textarea placeholder bug
	/* istanbul ignore if */
	if (isIE &&
		currentParent.tag === 'textarea' &&
		currentParent.attrsMap.placeholder === text
	) {
		return
	}
	var children = currentParent.children;
	text = inPre || text.trim() ?
		isTextTag(currentParent) ? text : decodeHTMLCached(text)
		// only preserve whitespace if its not right after a starting tag
		:
		preserveWhitespace && children.length ? ' ' : '';
	if (text) {
		var res;
		if (!inVPre && text !== ' ' && (res = parseText(text, delimiters))) {
			children.push({
				type: 2,
				expression: res.expression,
				tokens: res.tokens,
				text: text
			});
		} else if (text !== ' ' || !children.length || children[children.length - 1].text !== ' ') {
			children.push({
				type: 3,
				text: text
			});
		}
	}
}

當解析器遇到文本節(jié)點時,如上代碼中的 chars 鉤子函數(shù)就會被調用,并且接收該文本節(jié)點的文本內容作為參數(shù)。

我們來看chars鉤子函數(shù)最開始的這段代碼:

if (!currentParent) {
	{
		if (text === template) {
			warnOnce(
				'Component template requires a root element, rather than just text.'
			);
		} else if ((text = text.trim())) {
			warnOnce(
				("text \"" + text + "\" outside root element will be ignored.")
			);
		}
	}
	return
}

首先判斷了 currentParent 變量是否存在,我們知道 currentParent 變量指向的是當前節(jié)點的父節(jié)點:。

如果 currentParent 變量不存在說明什么問題?

  • 1:沒有根元素,只有文本。
  • 2: 文本在根元素之外。

當遇到第一種情況打印警告信息:"模板必須要有根元素",第二種情況打印警告信息:" 根元素外的文本將會被忽略"。

接下來:

if (isIE &&
	currentParent.tag === 'textarea' &&
	currentParent.attrsMap.placeholder === text
) {
	return
}

這段代碼是用來解決 IE 瀏覽器中渲染 <textarea> 標簽的 placeholder 屬性時存在的 bug 的。具體的問題大家可以在這個 issue 查看。

接下來是個嵌套三元表達式:

var children = currentParent.children;
text = inPre || text.trim() ?
	isTextTag(currentParent) ? text : decodeHTMLCached(text)
	// only preserve whitespace if its not right after a starting tag
	:
	preserveWhitespace && children.length ? ' ' : '';

這個嵌套三元表達式判斷了條件 inPre || text.trim() 的真假,如果為 true,檢測了當前文本節(jié)點的父節(jié)點是否是文本標簽,如果是文本標簽則直接使用原始文本,否則使用decodeHTMLCached 函數(shù)對文本進行解碼。

inPre || text.trim() 如果為 false,檢測 preserveWhitespace 是否為 true 。preserveWhitespace 是一個布爾值代表著是否保留空格,只有它為真的情況下才會保留空格。但即使 preserveWhitespace 常量的值為真,如果當前節(jié)點的父節(jié)點沒有子元素則也不會保留空格,換句話說,編譯器只會保留那些 不存在于開始標簽之后的空格。

接下來:

if (text) {
	var res;
	if (!inVPre && text !== ' ' && (res = parseText(text, delimiters))) {
		children.push({
			type: 2,
			expression: res.expression,
			tokens: res.tokens,
			text: text
		});
	} else if (text !== ' ' || !children.length || children[children.length - 1].text !== ' ') {
		children.push({
			type: 3,
			text: text
		});
	}
}

這里相當就比較簡單了一個 if else if 操作,第一個 if 判斷當前元素未使用v-pre 指令,text不為空,使用 parseText 函數(shù)成功解析當前文本節(jié)點的內容。

對于前兩個條件很好理解,關鍵在于 parseText 函數(shù)能夠成功解析文本節(jié)點的內容說明了什么,如下示例代碼:

<div> hello: {{ message }} </div>

如上模板中存在的文本節(jié)點包含了 Vue 語法中的字面量表達式,而 parseText 函數(shù)的作用就是用來解析這段包含了字面量表達式的文本的。此時會執(zhí)行以下代碼創(chuàng)建一個類型為2(type = 2) 的元素描述對象:

children.push({
	type: 2,
	expression: res.expression,
	tokens: res.tokens,
	text: text
});

注意:類型為 2 的元素描述對象擁有三個特殊的屬性,分別是 expression 、tokens 以及text ,其中 text 就是原始的文本內容,而 expression 和 tokens 的值是通過 parseText 函數(shù)解析的結果中讀取的。

后面我們專門會講講parseText函數(shù),接下來繼續(xù)看下如果上列的 if 判斷失敗出現(xiàn)的三種可能性。

  • 當前解析的元素使用v-pre 指令
  • text 為空
  • parseText 解析失敗

只要以上三種情況中,有一種情況出現(xiàn)則代碼會來到else...if 分支的判斷,如下:

else if (text !== ' ' || !children.length || children[children.length - 1].text !== ' ') {
	children.push({
		type: 3,
		text: text
	});
 }

如果滿足 else if 中的條件直接,創(chuàng)建一個類型為3(type = 3) 的元素描述對象:類型為3 的元素描述對象只擁有一個的屬性text存儲原始的文本內容。

在看下要滿足 else if 中的這些條件吧!

  • 文本內容不是空格
  • 文本內容是空格,但是該文本節(jié)點的父節(jié)點還沒有子節(jié)點(即 !children.length )
  • 文本內容是空格,并且該文本節(jié)點的父節(jié)點有子節(jié)點,但最后一個子節(jié)點不是空格

接下來我們來聊聊之前講到的parseText 函數(shù)。

parseText

function parseText(
	text,
	delimiters
) {
	var tagRE = delimiters ? buildRegex(delimiters) : defaultTagRE;
	if (!tagRE.test(text)) {
		return
	}
	var tokens = [];
	var rawTokens = [];
	var lastIndex = tagRE.lastIndex = 0;
	var match, index, tokenValue;
	while ((match = tagRE.exec(text))) {
		index = match.index;
		// push text token
		if (index > lastIndex) {
			rawTokens.push(tokenValue = text.slice(lastIndex, index));
			tokens.push(JSON.stringify(tokenValue));
		}
		// tag token
		var exp = parseFilters(match[1].trim());
		tokens.push(("_s(" + exp + ")"));
		rawTokens.push({
			'@binding': exp
		});
		lastIndex = index + match[0].length;
	}
	if (lastIndex < text.length) {
		rawTokens.push(tokenValue = text.slice(lastIndex));
		tokens.push(JSON.stringify(tokenValue));
	}
	return {
		expression: tokens.join('+'),
		tokens: rawTokens
	}
}

parseText 接收兩個參數(shù) text 要解析的文本,delimiters 是編譯器的一個用戶自定義選項delimiters ,通過它可以改變文本插入分隔符。所以才有了如下代碼。

var tagRE = delimiters ? buildRegex(delimiters) : defaultTagRE;

這里是解析文本所用正則之間的一個較量,delimiters 有值就調用buildRegex函數(shù),我們默認是沒有值,使用 defaultTagRE 來解析文本。

var defaultTagRE = /\{\{((?:.|\r?\n)+?)\}\}/g;

這個正則還是非常簡單,接下來會判斷,如果文本中沒有與正則相匹配的文本直接直接終止函數(shù)的執(zhí)行。

if (!tagRE.test(text)) {
    return
}

接下來代碼就有意思了一起看下。

var tokens = [];
var rawTokens = [];
var lastIndex = tagRE.lastIndex = 0;
var match, index, tokenValue;
while ((match = tagRE.exec(text))) {
	index = match.index;
	// push text token
	if (index > lastIndex) {
		rawTokens.push(tokenValue = text.slice(lastIndex, index));
		tokens.push(JSON.stringify(tokenValue));
	}
	// tag token
	var exp = parseFilters(match[1].trim());
	tokens.push(("_s(" + exp + ")"));
	rawTokens.push({
		'@binding': exp
	});
	lastIndex = index + match[0].length;
}
if (lastIndex < text.length) {
	rawTokens.push(tokenValue = text.slice(lastIndex));
	tokens.push(JSON.stringify(tokenValue));
}
return {
	expression: tokens.join('+'),
	tokens: rawTokens
}

這段代碼不難,初始定義了一系列變量。 接著開啟一個while循環(huán),使用 tagRE 正則匹配文本內容,并將匹配結果保存在 match 變量中,直到匹配失敗循環(huán)才會終止,這時意味著所有的字面量表達式都已經處理完畢了。

在這個while循環(huán)結束返回一個對象,expression、tokens分別存儲解析過程中的信息。

假設文本如下:

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

parseText 解析文本后返回的對象。

{
  expression: "'hello'+_s(message)",
  tokens: [
    'hello',
    {
      '@binding': 'message'
    }
  ]
}

接下來我們聊聊對結束標簽的處理。

end 源碼

end: function end() {
	// remove trailing whitespace
	var element = stack[stack.length - 1];
	var lastNode = element.children[element.children.length - 1];
	if (lastNode && lastNode.type === 3 && lastNode.text === ' ' && !inPre) {
		element.children.pop();
	}
	// pop stack
	stack.length -= 1;
	currentParent = stack[stack.length - 1];
	closeElement(element);
}

end 鉤子函數(shù),當解析 html 字符串遇到結束標簽的時候,。那么在 end 鉤子函數(shù)中都需要做哪些事情呢?

在之前的文章中我們講過解析器遇到非一元標簽的開始標簽時,會將該標簽的元素描述對象設置給 currentParent 變量,代表后續(xù)解析過程中遇到的所有標簽都應該是 currentParent 變量所代表的標簽的子節(jié)點,同時還會將該標簽的元素描述對象添加到 stack 棧中。

而當遇到結束標簽的時候則意味著 currentParent 變量所代表的標簽以及其子節(jié)點全部解析完畢了,此時我們應該把 currentParent 變量的引用修改為當前標簽的父標簽,這樣我們就將作用域還原給了上層節(jié)點,以保證解析過程中正確的父子關系。

下面代碼就是來完成這個工作:

stack.length -= 1;
currentParent = stack[stack.length - 1];
closeElement(element);

首先將當前節(jié)點出棧:stack.length -= 1 什么意思呢?

看一個代碼就懂了。

var arr = [1,2,3,4];
arr.length-=1;
>arr [1,2,3]

接著讀取出棧后 stack 棧中的最后一個元素作為 currentParent 變量的值。 那closeElement 函數(shù)是做什么用的呢?

closeElement 源碼

function closeElement(element) {
	// check pre state
	if (element.pre) {
		inVPre = false;
	}
	if (platformIsPreTag(element.tag)) {
		inPre = false;
	}
	// apply post-transforms
	for (var i = 0; i < postTransforms.length; i++) {
		postTransforms[i](element, options);
	}
}

closeElement 的作用有兩個:第一個是對數(shù)據(jù)狀態(tài)的還原,第二個是調用后置處理轉換鉤子函數(shù)。

接下來看下end函數(shù)中剩余代碼:

// remove trailing whitespace
var element = stack[stack.length - 1];
var lastNode = element.children[element.children.length - 1];
if (lastNode && lastNode.type === 3 && lastNode.text === ' ' && !inPre) {
    element.children.pop();
}

這個代碼的作用是去除當前元素最后一個空白子節(jié)點,我們在講解 chars 鉤子函數(shù)時了解到:preserveWhitespace 只會保留那些不在開始標簽之后的空格,所以當空白作為標簽的最后一個子節(jié)點存在時,也會被保留,如下代碼所示:

<div><span>test</span> <!-- 空白占位 -->  </div>

如上代碼中 <span> 標簽的結束標簽與 <div> 標簽的結束標簽之間存在一段空白,這段空白將會被保留。如果這段空白被保留那么就可能對布局產生影響,尤其是對行內元素的影響。

為了消除這些影響帶來的問題,好的做法是將它們去掉,而如代碼就是用來完成這個工作的。

comment 注釋節(jié)點描述對象

解析器是否會解析并保留注釋節(jié)點,是由 shouldKeepComment 編譯器選項決定的,開發(fā)者可以在創(chuàng)建Vue 實例的時候通過設置 comments 選項的值來控制編譯器的shouldKeepComment 選項。默認情況下 comments 選項的值為 false ,即不保留注釋,假如將其設置為 true ,則當解析器遇到注釋節(jié)點時會保留該注釋節(jié)點,此時 parseHTML 函數(shù)的 comment 鉤子函數(shù)會被調用,如下:

comment: function comment(text) {
	currentParent.children.push({
		type: 3,
		text: text,
		isComment: true
	});
}

要注意的是,普通文本節(jié)點與注釋節(jié)點的元素描述對象的類型是一樣的都是 3 ,不同的是注釋節(jié)點的元素描述對象擁有 isComment 屬性,并且該屬性的值為 true,目的就是用來與普通文本節(jié)點作區(qū)分的。

以上就是vue parseHTML源碼解析hars end comment鉤子函數(shù)的詳細內容,更多關于vue parseHTML鉤子函數(shù)的資料請關注腳本之家其它相關文章!

相關文章

  • 詳解Vue 方法與事件處理器

    詳解Vue 方法與事件處理器

    本篇文章主要介紹了詳解Vue 方法與事件處理器 ,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-06-06
  • Vue中實現(xiàn)動態(tài)更新JSON數(shù)據(jù)的三種方式

    Vue中實現(xiàn)動態(tài)更新JSON數(shù)據(jù)的三種方式

    在 Vue 項目中動態(tài)更新 JSON 數(shù)據(jù),可以通過以下幾種方式實現(xiàn),具體方法取決于你的需求,比如數(shù)據(jù)是存儲在前端還是后端、是否需要持久化等,文中通過代碼示例講解的非常詳細,需要的朋友可以參考下
    2025-04-04
  • Vue.js使用v-show和v-if的注意事項

    Vue.js使用v-show和v-if的注意事項

    這篇文章一開始先對Vue.js中v-show和v-if兩者的區(qū)別進行了簡單的介紹,而后通過圖文詳細給大家介紹了Vue.js使用v-show和v-if注意的事項,有需要的朋友們可以參考借鑒,下面來一起看看吧。
    2016-12-12
  • Vue中進行打包與部署的方法實例

    Vue中進行打包與部署的方法實例

    這篇文章主要給大家介紹了關于Vue中進行打包與部署的相關資料, 我們常使用前后端分離項目時,會需要將前端vue打包然后部署,需要的朋友可以參考下
    2023-09-09
  • Vue+Echarts實現(xiàn)分時圖和交易量圖的繪制

    Vue+Echarts實現(xiàn)分時圖和交易量圖的繪制

    近來發(fā)現(xiàn)Echarts?API越發(fā)的強大,對于繪制各類圖形可以使用Echarts實現(xiàn)。本文將利用Echarts實現(xiàn)分時圖和交易量圖的繪制,希望對大家有所幫助
    2023-03-03
  • vue子傳父關于.sync與$emit的實現(xiàn)

    vue子傳父關于.sync與$emit的實現(xiàn)

    這篇文章主要介紹了vue子傳父關于.sync與$emit的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-11-11
  • VeeValidate在vue項目里表單校驗應用案例

    VeeValidate在vue項目里表單校驗應用案例

    這篇文章主要介紹了VeeValidate在vue項目里表單校驗應用案例,VeeValidate是Vue.js的驗證庫,它有很多驗證規(guī)則,并支持自定義規(guī)則,非常具有實用價值,需要的朋友可以參考下
    2018-05-05
  • vue 實現(xiàn) ios 原生picker 效果及實現(xiàn)思路解析

    vue 實現(xiàn) ios 原生picker 效果及實現(xiàn)思路解析

    這篇文章主要介紹了vue 實現(xiàn) ios 原生picker 效果及實現(xiàn)思路解析,本文給大家介紹的非常詳細,具有參考借鑒價值,需要的朋友可以參考下
    2017-12-12
  • Vue中對<style scoped> 中的scoped屬性解析

    Vue中對<style scoped> 中的scoped屬性解析

    在Vue的單文件組件中,<style scoped> 的 scoped 屬性用于實現(xiàn)?樣式作用域隔離?,下面通過實例代碼講解Vue中對<style scoped>中的scoped屬性,感興趣的朋友一起看看吧
    2025-03-03
  • 項目中如何使用axios過濾多次重復請求詳解

    項目中如何使用axios過濾多次重復請求詳解

    在項目開發(fā)中經常需要處理重復點擊導致多次調用接口的問題,這篇文章主要介紹了項目中如何使用axios過濾多次重復請求的相關資料,需要的朋友可以參考下
    2021-07-07

最新評論

潞城市| 雷山县| 黎川县| 榆林市| 东光县| 油尖旺区| 凯里市| 隆子县| 绵阳市| 三亚市| 黄冈市| 开化县| 长子县| 镇坪县| 扶沟县| 金坛市| 米脂县| 新竹县| 辉县市| 新沂市| 正定县| 洛阳市| 崇礼县| 盐源县| 抚顺市| 保康县| 神木县| 卢湾区| 莱州市| 江津市| 蛟河市| 紫阳县| 河北省| 东乌珠穆沁旗| 喀喇沁旗| 噶尔县| 巩义市| 万安县| 天水市| 奈曼旗| 临沂市|