手撕Vue-實現事件相關指令

2023-10-21 18:01:11

經過上一篇文章的學習,實現了介面驅動資料更新,接下來實現一下其它相關的指令,比如事件相關的指令,v-on 這個指令的使用頻率還是很高的,所以我們先來實現這個指令。

v-on 的作用是什麼,是不是可以給某一個元素繫結一個事件。

緊接著瞭解了 v-on 的作用之後,我在 example.html 的結構程式碼當中新增了一個 div 用 v-on 繫結了一個點選事件,然後在 methods 當中新增了一個 myFn 的方法,然後在點選事件觸發的時候呼叫了 myFn 方法。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Vue基本模板</title>
    <script src="js/nue.js"></script>
</head>
<body>
<div id="app">
    <input type="text" v-model="name"/>
    <div v-on:click="myFn">我是div</div>
</div>

<script>
    let vue = new Nue({
        el: document.querySelector('#app'),
        data: {
            name: "BNTang"
        },
        methods: {
            myFn() {
                alert('myFn被執行了');
            },
        }
    });
</script>
</body>
</html>

如上已經將基本的結構搭建完畢了,現在需要做的事情就是需要處理一下 v-on 這個指令。

首先來看我們自己編寫的 Nue 原始碼,在建立 Nue 範例的時候, 呼叫了 new Compiler(this);,進入 Compiler,constructor 方法繼續往下看, 在進入 this.buildTemplate(fragment);,遍歷所有的節點,判斷是否是一個元素時,呼叫了 this.buildElement(node);, 進入 buildElement 方法,可以看到之前就是在這裡處理了 v-model 這個指令,現在我們需要在這裡處理 v-on 這個指令。

我先將 name, value 列印到控制檯,輸出結果如下:

type text
v-model name
v-on:click myFn

可以得出如果我們編寫的是 v-model,那麼 name 就是 v-model,value 就是 name,如果編寫的是 v-on:click,那麼 name 就是 v-on:click,value 就是 myFn。

知道了這些資訊之後就可以開展下一步了,我在將 name 按照 : 進行分割一次就會拿到的是 v-on 與 click,click 就是待會我們要註冊的事件型別,在用解構的方式將 name, value 取出來,程式碼如下:

let [directiveName, directiveType] = name.split(':');

directiveName 就是 v-on,directiveType 就是 click。

然後再將之前的程式碼 name.split('-'); 改寫為 directiveName.split('-');, 這個時候我們將解構出來的結果如下:

model
on

這個時候就可以在之前的工具類當中新增一個 on 方法, 來用處理 v-on,在新增 on 方法之前,改造一下根據指令名稱, 呼叫不同的處理常式的程式碼,將之前的程式碼改寫為如下:

CompilerUtil[directive](node, value, this.vm, directiveType);

多了一個 directiveType 引數,這個引數就是指令的型別,比如 v-on:click,那麼 directiveType 就是 click,這個時候就可以在工具類當中新增一個 on 方法了,程式碼如下:

on: function (node, value, vm, type) {
    node.addEventListener(type, (e) => {
        alert('事件註冊成功了');
    });
}

這個時候我們在頁面上點選 div 的時候,就會彈出一個提示框,說明事件註冊成功了。

事件註冊成功了是沒問題,但是這個事件執行的內容,是自己的,並不是通過 v-on 繫結的,所以我們需要將這個事件執行的內容改為通過 v-on 繫結的,這個時候就需要用到之前的 methods 物件了,我們需要通過 methods 物件來獲取到對應的方法,然後將這個方法執行。

接下來要改造一下建立 Nue 範例的時候,將 methods 儲存起來,改造一下 Nue 的建構函式,以後在根據對應的方法名稱,獲取到對應的方法, 再執行即可,程式碼如下:

this.$methods = options.methods;

改造完畢之後,我們就可以在工具類當中的 on 方法當中,通過 methods 物件獲取到對應的方法,然後執行即可,程式碼如下:

on: function (node, value, vm, type) {
    node.addEventListener(type, (e) => {
        vm.$methods[value](e);
    });
}

這個時候我們在頁面上點選 div 的時候,就會彈出一個提示框,說明事件註冊成功了,並且事件執行的內容也是通過 v-on 繫結的。

在 myFn 方法中列印一下 this,發現並不是 Nue 的範例,而是 myFn 本身:

這個時候就需要將 myFn 的 this 改為 Nue 的範例,這個時候就需要用到 call 方法了,程式碼如下:

node.addEventListener(type, (e) => {
    vm.$methods[value].call(vm, e);
});

call 方法的第一個引數是改變 this 的指向,第二個引數是傳遞的引數,這個時候我們在 myFn 方法中列印一下 this,發現已經是 Nue 的範例了。

到此為止,v-on 指令的實現已經完成了。