数据响应式是一种编程概念,在许多现代编程语言和框架中都有广泛应用,尤其是在前端开发领域。其本质确实如你所说,当数据发生变化时,自动运行一些相应的函数。

实现原理

  • 观察者模式

    • 数据响应式通常基于观察者模式实现。数据被视为被观察的对象,而那些在数据变化时需要执行的函数则是观察者。当数据发生变化时,通知所有注册的观察者执行相应的操作。
    • 例如,在 Vue.js 中,通过使用 ES6 的 Proxy 对象或 Object.defineProperty 方法来拦截对数据的访问和修改,当数据被修改时,触发依赖收集过程,通知相关的组件重新渲染。
  • 依赖收集与触发

    • 在数据响应式系统中,当一个函数依赖于某个特定的数据时,系统会记录这种依赖关系。当数据发生变化时,系统能够准确地找到依赖于该数据的函数,并触发它们执行。
    • 以 Vue.js 为例,当一个组件的模板中使用了某个数据,在组件渲染过程中,会建立对该数据的依赖。当数据变化时,Vue.js 能够快速确定哪些组件需要重新渲染,并执行相应的渲染函数。

手写一个简单的数据响应式程序

/**
 * 观察一个对象,并为其属性创建 getter 和 setter
 * 当属性被读取时,会进行依赖收集
 * 当属性被修改时,会触发所有收集到的依赖函数
 *
 * @param {Object} obj - 要观察的对象
 */
function observe(obj) {
    for (const key in obj) {
        let internalValue = obj[key];
        let funcs = new Set();
        Object.defineProperty(obj, key, {
            get: function () {
                // 依赖收集
                if (window.__func) {
                    funcs.add(window.__func);
                }
                return internalValue;
            },
            set: function (value) {
                internalValue = value;
                // 派发更新
                for (const func of funcs) {
                    func();
                }
                funcs.clear();
            }
        });

    }
}
/**
 * 自动执行一个函数,并在执行完毕后清理
 *
 * @param {Function} func - 要自动执行的函数
 *
 * @example
 * autoFun(test);
 */
function autoFunc(func) {
    window.__func = func;
    func();
    window.__func = null;
}
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>手写简单的数据响应式</title>
</head>

<body>
    <div class="username">{{user.name}}</div>
    <script src="index.js"></script>
    <script>

        // 创建一个名为 user 的对象,包含 name 和 age 属性
        var user = {
            name: "John",
            age: 30
        }

        // 调用 observe 函数,对 user 对象进行监听
        observe(user);

        // 定义一个名为 test 的函数,用于更新页面上显示的用户名
        function test() {
            document.querySelector(".username").innerHTML = user.name;
        }

        // 调用 autoFunc 函数,自动执行 test 函数,并将 test 函数作为依赖添加到 observe 函数中
        autoFunc(test);

        // 更新 user 对象的 name 属性值
        user.name = "Jane";

    </script>
</body>

</html>

代码分析

  • observe 函数: 这个函数用于观察一个对象的所有属性。对于对象中的每个属性,它使用 Object.defineProperty 来定义一个 getter 和 setter。getter 用于收集依赖(即当前正在访问该属性的函数),setter 用于在属性值变化时更新依赖。它使用了一个 for...in 循环来遍历对象的每个属性。
  • autoFunc 函数: 这个函数用于自动执行一个函数,并在执行完毕后清理。它通过将函数赋值给一个临时属性 window.__func,模拟了一个全局变量,使得 observe 函数中的 getter 能够收集到当前正在访问的函数。在执行完函数后,它将 window.__func 设置回 null,以便进行下一次的依赖收集。
  • 使用示例: 代码的后半部分是 observe 和 autoFunc 函数的使用示例。它创建了一个 user 对象,然后使用 observe(user) 来观察它。接着,它定义了一个 test 函数,用于更新文档中某个元素的内容。通过调用 autoFunc(test),test 函数被执行,并且由于 window.__func 的设置,它被添加到 user.name 的依赖集合中。随后,当 user.name 的值被更改为 "Jane" 时,test 函数会因为 observe 函数中定义的 setter 逻辑而自动再次执行。
Last modification:August 26, 2024
如果觉得我的文章对你有用,请随意赞赏