第142篇:原生js实现响应式原理

好家伙,狠狠地补一下代码量

本篇我们来尝试使用原生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.补充

一张响应式原理图