第142篇:原生js實現響應式原理

养肥胖虎發表於2024-03-11

好傢伙,狠狠地補一下程式碼量

本篇我們來嘗試使用原生js實現vue的響應式

使用原生js,即代表沒有v-bind,v-on,也沒有v-model,所有語法糖我們都用原生實現

1.給輸入框綁個變數

<body>
    <input id="input_1"></input>
</body>
<script>
    let datavalue = "66666"
    const input_1 = document.getElementById("input_1")
    input_1.value = datavalue
    input_1.addEventListener('input', function(e) {
        datavalue = e.target.value
        console.log(datavalue)
    })

    
</script>

誒,似乎這樣就完成了

但我們要讓他更像vue

2.加上Dep,Watcher

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <input id="input_1"></input>
</body>
<script>
    // 模擬 Vue 例項
    let data = {
        message: 'Hello'
    };
    const input_1 = document.getElementById("input_1")
    input_1.value = data.message
    input_1.addEventListener('input', function (e) {
        e.target.value = data.message
        console.log(datavalue)
    })

    function defineReactive(obj, key, value) {
        let dep = new Dep(); // 依賴容器

        Object.defineProperty(obj, key, {
            get: function () {
                if (Dep.target) {
                    dep.addDep(Dep.target);
                }
                return value;
            },
            set: function (newValue) {
                value = newValue;
                dep.notify();
            }
        });
    }

    // 依賴容器
    function Dep() {
        this.deps = [];

        this.addDep = function (dep) {
            this.deps.push(dep);
        };

        this.notify = function () {
            this.deps.forEach(dep => {
                dep.update();
            });
        };
    }

    Dep.target = null;

    // Watcher
    function Watcher(updateFunc) {
        this.update = updateFunc;
    }

    // 初始化響應式資料
    defineReactive(data, 'message', data.message);

    // 模擬 Watcher
    let watcher = new Watcher(function () {
        console.log('Message updated:', data.message);
        input_1.value = data.message
    });

    // 模擬檢視更新
    Dep.target = watcher;
    data.message; // 觸發依賴收集
    setTimeout(() => {
        data.message = '6666'; //觸發更新
    }, 1000)
</script>

</html>

3.效果圖

4.程式碼解釋

  1. defineReactive 函式用來定義一個響應式屬性,其中透過 Object.defineProperty 給屬性新增 getter 和 setter 方法。在 getter 方法中,會判斷 Dep.target 是否存在,如果存在則將當前 Watcher 物件新增到依賴容器 Dep 中;在 setter 方法中,更新屬性的值,並透過依賴容器 Dep 的 notify 方法通知所有依賴的 Watcher 進行更新。

  2. Dep 函式是一個簡單的依賴容器,其中包含了一個 deps 陣列用來儲存依賴(Watcher),addDep 方法用來新增依賴,notify 方法用來通知所有依賴進行更新。

  3. Watcher 函式用來建立 Watcher 物件,其中包含一個 update 方法,用來在屬性發生變化時執行相應的更新操作。

  4. 在初始化響應式資料時,呼叫 defineReactive 函式定義了一個名為 message 的響應式屬性。

  5. 建立了一個 Watcher 物件 watcher,並在其建構函式中定義了一個回撥函式,用來在屬性變化時輸出訊息並更新檢視。

  6. watcher 賦值給 Dep.target,然後訪問 data.message,觸發依賴收集,將 watcher 新增到依賴容器 Dep 中。

5.補充

一張響應式原理圖

相關文章