数据响应式是一种编程概念,在许多现代编程语言和框架中都有广泛应用,尤其是在前端开发领域。其本质确实如你所说,当数据发生变化时,自动运行一些相应的函数。
实现原理
观察者模式
- 数据响应式通常基于观察者模式实现。数据被视为被观察的对象,而那些在数据变化时需要执行的函数则是观察者。当数据发生变化时,通知所有注册的观察者执行相应的操作。
- 例如,在 Vue.js 中,通过使用 ES6 的 Proxy 对象或 Object.defineProperty 方法来拦截对数据的访问和修改,当数据被修改时,触发依赖收集过程,通知相关的组件重新渲染。
依赖收集与触发
- 在数据响应式系统中,当一个函数依赖于某个特定的数据时,系统会记录这种依赖关系。当数据发生变化时,系统能够准确地找到依赖于该数据的函数,并触发它们执行。
- 以 Vue.js 为例,当一个组件的模板中使用了某个数据,在组件渲染过程中,会建立对该数据的依赖。当数据变化时,Vue.js 能够快速确定哪些组件需要重新渲染,并执行相应的渲染函数。
手写一个简单的数据响应式程序
代码语言:javascript复制/**
* 观察一个对象,并为其属性创建 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;
}
代码语言:javascript复制<!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 逻辑而自动再次执行。