手撕Vue-查詢指令和模板

BNTang發表於2023-10-15

接著上一篇文章,我們已經實現了提取元素到記憶體的過程,接下來我們要實現的是查詢指令和模板。

大致的思路是這樣的:

  1. 遍歷所有的節點
  2. 需要判斷當前遍歷到的節點是一個元素還是一個文字
  3. 如果是一個元素, 我們需要判斷有沒有v-model屬性
  4. 如果是一個文字, 我們需要判斷有沒有{{}}的內容

那麼隨著思路的展開,接下來我們就來實現這個功能。

首先我們編寫一個 buildTemplate 方法,主要功能是利用指定的資料編譯記憶體中的元素:

buildTemplate(fragment) {
    let nodeList = [...fragment.childNodes];

    // 1.遍歷所有的節點
    nodeList.forEach(node => {

    });
}

buildTemplate 方法定義在 Compiler 類中,我們需要在 compile 方法中呼叫它:

image-20231015103931327

// 2.利用指定的資料編譯記憶體中的元素
this.buildTemplate(fragment);

然後我們在 buildTemplate 方法中完善我們的程式碼,這裡我就先直接上完整的實現程式碼:

buildTemplate(fragment) {
    let nodeList = [...fragment.childNodes];

    // 1.遍歷所有的節點
    nodeList.forEach(node => {
        // 2.需要判斷當前遍歷到的節點是一個元素還是一個文字
        if (this.vm.isElement(node)) {
            // 是一個元素
            this.buildElement(node);
        } else {
            // 不是一個元素
            this.buildText(node);
        }
    });
}

buildElement(node) {
    // 可以透過 node.attributes 獲取到當前元素上所有的屬性
    let attrs = [...node.attributes];

    // 1.遍歷所有的屬性
    attrs.forEach(attr => {
        let {name, value} = attr;
        if (name.startsWith('v-')) {
            console.log('是Vue的指令, 需要我們處理', name);
        }
    });
}

buildText(node) {
    // 可以透過 node.textContent 獲取到當前文字節點的內容
    let content = node.textContent;

    // 編寫一個正規表示式, 用來匹配 {{}}
    // 如下正規表示式的含義是: 匹配 {{}} 中間的內容
    // /: 正規表示式通常以斜槓 / 開始和結束,表示正規表示式的開始和結束。
    // \{ 和 \}: 這些是跳脫字元,用於匹配實際的花括號 { 和 }。花括號在正規表示式中具有特殊意義,因此需要使用反斜槓進行轉義。
    // \{\{ 和 \}\}: 這是正規表示式的起始和結束部分,用於匹配雙花括號 {{ 和 }}。
    // .+?: 這部分用於匹配雙花括號內的任意字元,. 表示匹配任意字元,+ 表示匹配一個或多個前面的字元,? 表示非貪婪匹配,即儘可能匹配最短的內容。這樣確保匹配到最近的結束雙花括號 }}。
    // /g: g 是正規表示式的標誌,表示全域性匹配,即匹配字串中的所有符合條件的部分。
    // /i: i 也是正規表示式的標誌,表示不區分大小寫匹配,這意味著 {{...}} 和 {{...}} 都會被匹配到。
    // 因此,這個正規表示式可以用於在字串中找到並提取所有的 {{...}} 結構,不區分大小寫,不貪婪匹配,且匹配所有出現的情況。
    let reg = /\{\{.+?\}\}/gi;
    if (reg.test(content)) {
        console.log('是一個文字節點, 需要我們處理', content);
    }
}

好了,我們來看一下效果,我們在瀏覽器中開啟,然後開啟控制檯,可以看到如下的效果:

image-20231015104503123

發現,只有 v-model 指令被處理, {{}} 沒有被處理,如下圖我框出了 <p>

image-20231015104624387

也就是說我們迴圈節點的時候,只迴圈了一層,沒有迴圈到 <p> 標籤中的文字節點,所以我們需要修改一下 buildTemplate 方法, 讓它支援遞迴,處理子元素(處理後代):

image-20231015104809104

// 處理子元素(處理後代)
this.buildTemplate(node);

改造後,我們再來看一下效果,可以看到 {{}} 也被處理了:

image-20231015104833637

好了,到這裡我們就實現了查詢指令和模板的功能,下一篇我們來繼續完善一下我們的不完整的程式碼,一步一步來慢慢撕。

相關文章