成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

【Vue原理】Compile - 源碼版 之 Parse 標(biāo)簽解析

loostudy / 1769人閱讀

摘要:當(dāng)字符串開頭是時(shí),可以匹配匹配尾標(biāo)簽。從結(jié)尾,找到所在位置批量閉合。

寫文章不容易,點(diǎn)個(gè)贊唄兄弟  


專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧
研究基于 Vue版本 【2.5.17】

如果你覺得排版難看,請(qǐng)點(diǎn)擊 下面鏈接 或者 拉到 下面關(guān)注公眾號(hào)也可以吧

【Vue原理】Compile - 源碼版 之 標(biāo)簽解析

咳咳,上一篇文章,我們已經(jīng)大致把 parse 的流程給記錄了一遍,如果沒看過,比較建議,先把這個(gè)流程給看了
Compile - 源碼版 之 Parse 主要流程

但是忽略了其中的處理細(xì)節(jié),比如標(biāo)簽怎么解析的,屬性怎么解析的,而且這兩個(gè)內(nèi)容也是非常多的,所以需要多帶帶拎出來(lái)詳細(xì)記錄,不然混在一起,又臭又長(zhǎng)

白話版在這~ Compile - 白話版

今天的內(nèi)容是,記錄 標(biāo)簽解析的 源碼

首先,開篇之前呢,我們來(lái)了解一下文章會(huì)出現(xiàn)過的正則

相關(guān)正則
var ncname = "[a-zA-Z_][w-.]*";

var qnameCapture = "((?:" + ncname + ":)?" + ncname + ")";

var startTagOpen = new RegExp(("^<" + qnameCapture));

var startTagClose = /^s*(/?)>/;

var endTag = new RegExp(("^]*>"));

var attribute = /^s*([^s""<>/=]+)(?:s*(=)s*(?:"([^"]*)"+|"([^"]*)"+|([^s""=<>`]+)))?/;

主要是四個(gè)

startTagOpen

匹配 頭標(biāo)簽的 前半部分。當(dāng)字符串開頭是 頭標(biāo)簽時(shí),可以匹配

startTagClose

匹配 頭標(biāo)簽的 右尖括號(hào)。當(dāng)字符串開頭是 > 時(shí),可以匹配

endTag

匹配 尾標(biāo)簽。當(dāng)字符串開頭是 尾標(biāo)簽時(shí) 可以匹配

attribute

匹配標(biāo)簽上的屬性。當(dāng)字符串開頭是屬性則可以匹配

好的,看完上面四個(gè)正則, 心里有個(gè) * 數(shù)之后,相信下面的內(nèi)容你會(huì)更加清晰些

下面的內(nèi)容分為

1、循環(huán)遍歷 template

2、處理 頭標(biāo)簽

3、處理 尾標(biāo)簽

那么我們按一個(gè)個(gè)來(lái)說

循環(huán)遍歷template

通過上一篇內(nèi)容,已經(jīng)記錄過 是怎么循環(huán)遍歷 template 的了,就是通過 parseHTML 這個(gè)方法

這個(gè)方法,因?yàn)閮?nèi)容需要,也記錄一遍

首先,什么是循環(huán)遍歷template?

template 是一個(gè)字符串,所以每匹配完一個(gè)信息(比如頭標(biāo)簽等),就會(huì)把template 截?cái)嗟狡ヅ涞慕Y(jié)束位置

比如 template 是

"
1111
"

當(dāng)我們匹配完了 頭標(biāo)簽,那么 template 就會(huì)被截?cái)喑?/p>

"1111
"

然后就這樣一直循環(huán)匹配新的 template,直到 template 被截?cái)喑?空字符串,那么匹配完畢,其中跟截?cái)嘤嘘P(guān)的一個(gè)重要函數(shù)就是 advance

這個(gè)函數(shù)在下面的源碼中用得非常多,需要牢記

其作用就是

1、截?cái)鄑emplate

2、保存當(dāng)前截?cái)嗟奈恢?。比如你匹配了template到 字符串長(zhǎng)度為5 的位置,那么 index 就是 4(從0開始)

function advance(n) {
    index += n;
    html = html.substring(n);
}

記住這個(gè)函數(shù)哦,我傳入一個(gè)數(shù)字 n,就是要把 template 從 n 截取到結(jié)尾

然后下面就看看簡(jiǎn)化的 parseHTML 源碼(如果嫌長(zhǎng),先跳到分析)

function parseHTML(html, options) {    

    

    // 保存所有標(biāo)簽的對(duì)象信息,tagName,attr,這樣,在解析尾部標(biāo)簽的時(shí)候得到所屬的層級(jí)關(guān)系以及父標(biāo)簽

    var stack = [];    

    var index = 0;    

    var last;    

    

    while (html) {



        last = html;        

        var textEnd = html.indexOf("<");        



        // 如果開頭是 標(biāo)簽的 <

        if (textEnd === 0) {      

      

            /**
             * 如果開頭的 < 屬性尾標(biāo)簽
             * 比如 html = "
" * 匹配出 endTagMatch =["
", "div"] */ * 如果開頭的 < 屬性尾標(biāo)簽 * 比如 html = "
" * 匹配出 endTagMatch =["
", "div"] */ var endTagMatch = html.match(endTag); if (endTagMatch) { var curIndex = index; // endTagMatch[0]="" advance(endTagMatch[0].length); // endTagMatch[1]="div" parseEndTag(endTagMatch[1], curIndex, index); continue } /** * 如果開頭的 < 屬性 頭標(biāo)簽 * parseStartTag 作用是,匹配標(biāo)簽存在的屬性,截?cái)?template * html = "
" * startTagMatch = {tagName: "div", attrs: []} */ * 如果開頭的 < 屬性 頭標(biāo)簽 * parseStartTag 作用是,匹配標(biāo)簽存在的屬性,截?cái)?template * html = "
" * startTagMatch = {tagName: "div", attrs: []} */ var startTagMatch = parseStartTag(); if (startTagMatch) { handleStartTag(startTagMatch); continue } } var text ,rest ,next ; // 模板起始位置 不是 <,而是文字 if (textEnd >= 0) { text = html.substring(0, textEnd); advance(textEnd); } // 處理文字,上篇文章已經(jīng)講過 if (options.chars && text) { options.chars(text); } } function parseStartTag(){...} function handleStartTag(){...} function parseEndTag(){...} }

這段代碼已經(jīng)簡(jiǎn)化得很簡(jiǎn)單了,算是整體對(duì) template 處理的一種把控我覺得

先匹配 < 的位置

1 如果 < 在template開頭

那么就是標(biāo)簽(這里先不討論 字符串中的 <)

然后需要多一層判斷

如果是尾標(biāo)簽的 <,那么交給 parseEndTag 處理

如果是頭標(biāo)簽的 <,那么使用 handleStartTag 處理

2 如果 < 不在 template 開頭

那么表明 開頭到 < 的這段位置是字符串,但是本文內(nèi)容是標(biāo)簽解析,所以忽略這部分

然后每完成一次匹配,就需要調(diào)用 advace 去截?cái)?template

然后現(xiàn)在,我們假定有下面這段處理

template = "
111
" parseHTML(template)

匹配 < 在開頭,正則判斷之后,發(fā)現(xiàn)不是 尾標(biāo)簽的 <,那么需要判斷是不是 頭標(biāo)簽的

然后使用 parseStartTag 方法去匹配頭標(biāo)簽信息

匹配成功,使用 handleStartTag 方法處理

看到在 parseHTML 末尾聲明了三個(gè)函數(shù),為了避免太長(zhǎng),我挑了出來(lái)放在相應(yīng)的內(nèi)容講

而之所以會(huì)在里面聲明這個(gè)三個(gè)函數(shù),是為了在這三個(gè)函數(shù)中,能訪問到 parseHTML 中的變量,比如 stack,index

處理頭標(biāo)簽 parseStartTag

這個(gè)方法的作用就是

1、把頭標(biāo)簽的所有信息集合起來(lái),包括屬性,標(biāo)簽名等

2、匹配完成之后同樣調(diào)用 advance 去截?cái)?template

3、把標(biāo)簽信息 返回

源碼已經(jīng)簡(jiǎn)化,并且有做流程注釋,大家肯定看得懂,太煩的可以看后面的結(jié)果

function parseStartTag() {    



    // html ="
111
" // start = [" while ( // 匹配不到頭標(biāo)簽的 >,開始匹配 屬性內(nèi)容 // end = null ! (end = html.match(startTagClose)) && // 開始匹配 屬性內(nèi)容 // attr = ["name=1", "name", "=" ] (attr = html.match(attribute)) ) { advance(attr[0].length); match.attrs.push(attr); } // 匹配到 起始標(biāo)簽的 >,標(biāo)簽屬性那些已經(jīng)匹配完畢了 // 返回收集到的 標(biāo)簽信息 if (end) { advance(end[0].length); // 如果是單標(biāo)簽,那么 unarySlash 的值是 /,比如 match.unarySlash = end[1]; match.end = index; return match } } }

我們來(lái)記錄下這個(gè)方法會(huì)返回什么

比如

html = "
"

parseStartTag 處理之后會(huì)返回以下內(nèi)容

{    

    tagName: "div",    

    attrs: [

        [" name=1", "name", "=" ,

         undefined, undefined, "1"]

    ],    

    unarySlash: "",    

    start: 0,    

    end: 12

}

其中 的屬性

start:頭標(biāo)簽的 < 在 template 中的位置

end:頭標(biāo)簽的 > 在 tempalte 中的位置

attrs:是一個(gè)二維數(shù)組,存放著所有頭標(biāo)簽的 屬性信息

unarySlash:表示這個(gè)標(biāo)簽是否是 單標(biāo)簽。如果是 true,那么不是單標(biāo)簽。如果是 false,那么就是單標(biāo)簽。一切在于匹配頭標(biāo)簽時(shí),有沒有匹配到 /

通過 parseHTML 我們看到,parseStartTag 返回的 頭標(biāo)簽信息,給了誰(shuí)呢?

沒錯(cuò),傳給了 handleStartTag

handleStartTag

這個(gè)函數(shù)的作用

1、接收上一步收集的標(biāo)簽信息

2、處理屬性,轉(zhuǎn)換一下其格式

3、保存進(jìn) stack,記錄 DOM 父子結(jié)構(gòu)順序

function handleStartTag(match) {    



    var tagName = match.tagName;    

    var unarySlash = match.unarySlash;    



    // 判斷是不是單標(biāo)簽,input,img 這些

    var unary = isUnaryTag$$1(tagName) || !!unarySlash;    



    var l = match.attrs.length;    

    var attrs = new Array(l);    



    // 把屬性數(shù)組轉(zhuǎn)換成對(duì)象
    for (var i = 0; i < l; i++) {        

        var args = match.attrs[i];    

   

        // args = [" name=1", "name", "=",

                undefined, undefined, "1" ]        

        var value = args[3] || args[4] || args[5] || "";

        attrs[i] = {            

            name: args[1],            

            value: value

        };
    }    

    

    // 不是單標(biāo)簽,才存到 stack
    if (!unary) {
        stack.push({            

            tag: tagName,            

            attrs: attrs

        });
    }    



    if (options.start) {

        options.start(
            tagName, attrs, unary,
            match.start, match.end
        );
    }
}

最后,把該標(biāo)簽得到的信息,傳給 options.start,幫助建立 template 的 ast(在 上篇文章 Compile - 源碼版 之 Parse 主要流程 中有說明=)

那么到這里,頭標(biāo)簽 匹配完了

然后 template 被截?cái)喑?/p>

"111"

文本處理的部分我們跳過,跳到尾標(biāo)簽,所以 template 為

""

然后匹配到尾標(biāo)簽,交給 parseEndTag 處理

那么進(jìn)入我們的下一小節(jié)內(nèi)容,處理尾標(biāo)簽

處理尾標(biāo)簽

在 parseHTML 中看到

當(dāng)使用 endTag 這個(gè)正則成功匹配到尾標(biāo)簽時(shí),會(huì)調(diào)用 parseEndTag

而 這個(gè)函數(shù)呢,可能沒有那么好理解了,你可以先跳過源碼,翻到后面的解析

function parseEndTag(tagName, start, end) {    



    var pos, lowerCasedTagName;    



    // 從stack 最后查找匹配的 tagName 位置

    if (tagName) {        



        // 如果在 stack 中找不到,pos 最后是 -1

        for (pos = stack.length - 1; pos >= 0; pos--) {      

            if (stack[pos].tagName===tagName) break

        }
    }    

    else {        

        // 如果沒有提供標(biāo)簽名,那么關(guān)閉所有存在 stack 中的 起始標(biāo)簽

        pos = 0
    }    



    // 批量 stack pos 位置后的所有標(biāo)簽

    if (pos >= 0) {        



        // 關(guān)閉 pos 位置之后所有的起始標(biāo)簽,避免有些標(biāo)簽沒有尾標(biāo)簽

        // 比如 stack.len = 7 , pos=5 ,那么就關(guān)閉 最后兩個(gè)
        for (var i = stack.length - 1; i >= pos; i--) {    

            if (options.end) {

                options.end(stack[i].tag, start, end);
            }
        }        



        // 匹配完閉合標(biāo)簽之后,就把 匹配了的標(biāo)簽頭 給 移除了

        stack.length = pos;
    }
}

函數(shù)功能分為兩部分

1、找位置。從 stack 結(jié)尾,找到 tagName 所在位置 pos

2、批量閉合。閉合并移除 stack 在 pos 位置后的所有 tag

現(xiàn)在我們先給一個(gè)模板,然后慢慢解釋

現(xiàn)在已經(jīng)連續(xù)匹配到三個(gè) 頭標(biāo)簽,div,header,span

此時(shí) stack= [ div, header, span ]

然后開始匹配到 ,然后去 stack 末尾找 span

確定 span 在 stack 的位置 pos 后,批量閉合stack 的 pos 后的所有標(biāo)簽

為什么從末尾開始?

因?yàn)?stack 是按 template 的標(biāo)簽順序存放的,肯定是先匹配到父標(biāo)簽,再匹配到子標(biāo)簽

碰到 尾標(biāo)簽,肯定找最近匹配到的頭標(biāo)簽,那么肯定是剛存入 stack 的,那么就是在 stack 的結(jié)尾

為什么閉合 pos位置后所有標(biāo)簽?

因?yàn)榕掠械竺癫粚戦]合標(biāo)簽,比如模板是這樣

同樣,匹配完三個(gè)頭標(biāo)簽

stack = [ div, header, span ]

接著匹配到 ,于是在 stack 末尾中找 header

在倒數(shù)第二個(gè),那么 pos = 1

根據(jù) stack 的長(zhǎng)度, 遍歷一次 stack,閉合到末尾,就是閉合 stack[1], stack[2]

就是閉合 header 和 span 了

就是為了避免有屁民沒寫 尾標(biāo)簽

你說單標(biāo)簽沒有尾標(biāo)簽?。?/b>

是啊,但是單標(biāo)簽 不存進(jìn) stack 啊哈哈哈

在 handleStartTag 中有處理哦

接下來(lái)你就可以去看 parseEndTag 的源碼了,肯定能看懂

怎么閉合的呢?

parseEndTag 就做了匹配 tag 位置 和容錯(cuò)處理

主要實(shí)現(xiàn)閉合功能在調(diào)用 options.end 中,通過不斷地傳入尾標(biāo)簽從而完成閉合功能

如果你看過上篇文章 Compile - 源碼版 之 Parse 主要流程 就知道,閉合是為了形成正確的節(jié)點(diǎn)關(guān)系樹

也可以說,是為了明確節(jié)點(diǎn)的父子關(guān)系

總結(jié)

通過這次我們知道了

1、標(biāo)簽的匹配方法

2、怎么利用頭標(biāo)簽收集信息

3、閉合標(biāo)簽的處理方法

最后

鑒于本人能力有限,難免會(huì)有疏漏錯(cuò)誤的地方,請(qǐng)大家多多包涵,如果有任何描述不當(dāng)?shù)牡胤?,歡迎后臺(tái)聯(lián)系本人,有重謝

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/106588.html

相關(guān)文章

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

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

    Forest10 評(píng)論0 收藏0
  • Vue原理Compile - 源碼 Parse 屬性解析

    摘要:寫文章不容易,點(diǎn)個(gè)贊唄兄弟專注源碼分享,文章分為白話版和源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究基于版本如果你覺得排版難看,請(qǐng)點(diǎn)擊下面鏈接或者拉到下面關(guān)注公眾號(hào)也可以吧原理源碼版之屬性解析哈哈哈,今天終 寫文章不容易,點(diǎn)個(gè)贊唄兄弟 專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究...

    tinna 評(píng)論0 收藏0
  • Vue原理Compile - 源碼 optimize 標(biāo)記靜態(tài)節(jié)點(diǎn)

    摘要:一旦我們檢測(cè)到這些子樹,我們可以把它們變成常數(shù),這樣我們就不需要了在每次重新渲染時(shí)為它們創(chuàng)建新的節(jié)點(diǎn)在修補(bǔ)過程中完全跳過它們。否則,吊裝費(fèi)用將會(huì)增加好處大于好處,最好總是保持新鮮。 寫文章不容易,點(diǎn)個(gè)贊唄兄弟 專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究基于 Vue版本 【2.5.17】 如果你覺得排版難看,...

    Soarkey 評(píng)論0 收藏0
  • Vue原理Compile - 源碼 generate 節(jié)點(diǎn)數(shù)據(jù)拼接

    摘要:寫文章不容易,點(diǎn)個(gè)贊唄兄弟專注源碼分享,文章分為白話版和源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究基于版本如果你覺得排版難看,請(qǐng)點(diǎn)擊下面鏈接或者拉到下面關(guān)注公眾號(hào)也可以吧原理源碼版之節(jié)點(diǎn)數(shù)據(jù)拼接上一篇我們 寫文章不容易,點(diǎn)個(gè)贊唄兄弟 專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究...

    fizz 評(píng)論0 收藏0
  • Vue原理Compile - 源碼 generate 節(jié)點(diǎn)拼接

    摘要:還原的難度就在于變成模板了,因?yàn)槠渌氖裁吹仁窃獠粍?dòng)的哈哈,可是直接照抄最后鑒于本人能力有限,難免會(huì)有疏漏錯(cuò)誤的地方,請(qǐng)大家多多包涵,如果有任何描述不當(dāng)?shù)牡胤?,歡迎后臺(tái)聯(lián)系本人,有重謝 寫文章不容易,點(diǎn)個(gè)贊唄兄弟 專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究基于 Vue版本 【2.5.17】 如果你覺得排版...

    macg0406 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<