首頁 > 軟體

vue parseHTML函數原始碼解析 AST預備知識

2022-07-13 22:01:05

正文

接上章節:parseHTML 函數原始碼解析AST 基本形成

在正式扎進Vue parse原始碼之前,我們先了解下他周邊的工具函數, 這能幫我們快速的去理解閱讀。

還記得我們在上章節講的element元素節點的描述物件嗎?

var element = {
	type: 1,
	tag: tag,
	parent: null,
	attrsList: attrs,
	children: []
}

在原始碼中定義了一個createASTElement函數,用來建立一個元素的描述物件。

createASTElement函數

function createASTElement(tag, attrs, parent) {
		return {
			type: 1,
			tag: tag,
			attrsList: attrs,
			attrsMap: makeAttrsMap(attrs),
			parent: parent,
			children: []
		}
	}

解析指令所用正則

var onRE = /^@|^v-on:/;
var dirRE = /^v-|^@|^:/;
var forAliasRE = /([sS]*?)s+(?:in|of)s+([sS]*)/;
var forIteratorRE = /,([^,}]]*)(?:,([^,}]]*))?$/;
var stripParensRE = /^(|)$/g;
var argRE = /:(.*)$/;
var bindRE = /^:|^v-bind:/;
var modifierRE = /.[^.]+/g;

在解析開始標籤的時候你遇到的不僅有屬性,還有一些Vue 自定義的指令。下面一起來分析下解析指令會有用哪些正則。

onRE

var onRE = /^@|^v-on:/;

匹配以字元 @ 或 v-on: 開頭的字串,主要作用是檢測標籤屬性名是否是監聽事件的指令。

dirRE

var const dirRE = /^v-|^@|^:/

匹配以字元 v- 或 @ 或 : 開頭的字串,主要作用是檢測標籤屬性名是否是指令。在Vue中所有以 v- 開頭的屬性都被認為是指令,另外@字元是 v-on 的縮寫,: 字元是 v-bind 的縮寫。

forAliasRE

var forAliasRE = /([sS]*?)s+(?:in|of)s+([sS]*)/;

匹配 v-for 屬性的值,並捕獲 in 或 of 前後的字串。都是正則大神就不解釋怎麼捕獲的了。

forIteratorRE

var forIteratorRE = /,([^,}]]*)(?:,([^,}]]*))?$/;

這個也是匹配v-for的屬性值,不過比之前要稍微複雜點:列表渲染 v-for(https://cn.vuejs.org/v2/guide/list.html)需要先了解下這個。

//範例:1
<div v-for="(value, name) in object">
  {{ name }}: {{ value }}
</div>
//範例:2 
<div v-for="(value, name, index) in object">
  {{ index }}. {{ name }}: {{ value }}
</div>

沒錯就是用來捕獲,範例1中的:'obj , index' 範例2中的:'value, key, index' 。

stripParensRE

var stripParensRE = /^(|)$/g;

這個捕獲組用來捕獲要麼以字元 ( 開頭,要麼以字元 ) 結尾的字串,或者兩者都滿足。那麼這個正則的作用是什麼呢?我們在講解正則 forIteratorRE 時有個細節不知道大家注意到了沒有,就是 forIteratorRE 正則所匹配的字串是 'obj, index' ,而不是 '(obj, index)' ,這兩個字串的區別就在於第二個字串擁有左右括號,所以在使用 forIteratorRE 正則之前,需要使用 stripParensRE 正則去掉字串 '(obj, index)' 中的左右括號,實現方式很簡單:

"(obj, index)".replace(stripParensRE, "")

argRE

var argRE = /:(.*)$/;

argRE正則用來匹配指令編寫中的引數,並且擁有一個捕獲組,用來捕獲引數的名字。

範例:

<div v-on:click.item="handle"></div>

其中 v-on 為指令,click為傳遞給 v-on 指令的引數,stop 為修飾符。

bindRE

var bindRE = /^:|^v-bind:/;

該正則用來匹配以字元:或字串 v-bind: 開頭的字串,主要用來檢測一個標籤的屬性是否是繫結(v-bind)。

modifierRE

var modifierRE = /.[^.]+/g;

該正則用來匹配修飾符的,但是並沒有捕獲任何東西,但你可以用match、exec等方法獲取與當前正則匹配成功的資訊。

parse 函數中的變數

在講解 parse 函數直接我們還需要先了解下它內部所定義的一些變數以及用途。

function parse(template, options) {
	warn$2 = options.warn || baseWarn;
	platformIsPreTag = options.isPreTag || no;
	platformMustUseProp = options.mustUseProp || no;
	platformGetTagNamespace = options.getTagNamespace || no;
	transforms = pluckModuleFunction(options.modules, 'transformNode');
	preTransforms = pluckModuleFunction(options.modules, 'preTransformNode');
	postTransforms = pluckModuleFunction(options.modules, 'postTransformNode');
	delimiters = options.delimiters;
	var stack = [];
	var preserveWhitespace = options.preserveWhitespace !== false;
	var root;
	var currentParent;
	var inVPre = false;
	var inPre = false;
	var warned = false;
	function warnOnce(msg) {
        //...
	}
	function closeElement(element) {
       //...
	}
	parseHTML(template, {
		warn: warn$2,
		expectHTML: options.expectHTML,
		isUnaryTag: options.isUnaryTag,
		canBeLeftOpenTag: options.canBeLeftOpenTag,
		shouldDecodeNewlines: options.shouldDecodeNewlines,
		shouldDecodeNewlinesForHref: options.shouldDecodeNewlinesForHref,
		shouldKeepComment: options.comments,
		start: function start(tag, attrs, unary) {},
		end: function end() {},
		chars: function chars(text) {},
		comment: function comment(text) {},
	});
	return root
}

我們先來看下針對web平臺初始化的一些變數。

warn$2 = options.warn || baseWarn;
platformIsPreTag = options.isPreTag || no;
platformMustUseProp = options.mustUseProp || no;
platformGetTagNamespace = options.getTagNamespace || no;
transforms = pluckModuleFunction(options.modules, 'transformNode');
preTransforms = pluckModuleFunction(options.modules, 'preTransformNode');
postTransforms = pluckModuleFunction(options.modules, 'postTransformNode');
delimiters = options.delimiters;
  • warn$2 函數 毋庸置疑它作用是用來列印警告資訊的
  • platformIsPreTag 函數是一個編譯器選項,其作用是通過給定的標籤名字判斷該標籤是否是 pre 標籤。
  • platformMustUseProp 該函數也是一個編譯器選項,其作用是用來檢測一個屬性在標籤中是否要使用元素物件原生的 prop 進行繫結。
  • platformGetTagNamespace 該函數是一個編譯器選項,其作用是用來獲取元素(標籤)的名稱空間。
  • transforms 、preTransforms 、postTransforms 還沒講到它們的上下文,暫時不解釋它們的作用。
  • delimiters 它的值為 options.delimiters 屬性,它的值就是在建立 Vue 範例物件時所傳遞的 delimiters 選項。

繼續往下看:

var stack = [];
var preserveWhitespace = options.preserveWhitespace !== false;
var root;
var currentParent;
var inVPre = false;
var inPre = false;
var warned = false;
  • stack 初始值是一個空陣列,作用在上個章節我們講到,回退操作為了讓子元素描述物件的parent屬效能夠正確指向其父元素。
  • preserveWhitespace 是一個布林值並且它的值與編譯器選項中的options.preserveWhitespace選項有關,只要 options.preserveWhitespace 的值不為false,那麼 preserveWhitespace 的值就為真。其中 options.preserveWhitespace 選項用來告訴編譯器在編譯 html 字串時是否放棄標籤之間的空格,如果為 true 則代表放棄。
  • root 儲存最終生成的AST。
  • currentParent 通過上章節瞭解到,該變數維護元素描述物件之間的父子關係。
  • inVPre 初始值:false。標識當前解析的標籤是否在擁有 v-pre (跳過這個元素和它的子元素的編譯過程。)的標籤之內。
  • inPre 初始值:false。標識當前正在解析的標籤是否在 <pre></pre> 標籤之內。
  • warned 初始值:false。用來列印警告資訊的函數,只不過 warnOnce 函數就如它的名字一樣,只會列印一次警告資訊,並且 warnOnce 函數也是通過呼叫 warn 函數來實現的。

好了一些邊邊角角的東西就先講到這,接下來我們一起來分析Vue parse原始碼看看一顆完整的AST樹是如何構建出來的。更多關於vue parseHTML函數AST預備的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com