简易实现vue2和vue3的数据响应式
本文旨在理解vue观察者模式的实现原理, 欢迎讨论留言
本文代码可以直接复制到编辑器上运行, 在浏览器打印台上可以测试效果
vue2: Object.defineProperty 实现观察者模式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>defineProperty实现观察者模式</title>
</head>
<body>
<div id="container"></div>
<script>
function observer(target) {
const div = document.getElementById("container");
const ob = {};
const props = Object.keys(target);
function defineProp(prop) {
Object.defineProperty(ob, prop, {
get() {
return target[prop];
},
set(val) {
target[prop] = val;
render();
},
enumerable: true,
configurable: true,
});
}
for (const prop of props) {
defineProp(prop);
}
// 添加
function addProp(prop, value) {
if (!ob.hasOwnProperty(prop)) {
defineProp(prop);
target[prop] = value;
render();
}
}
// 删除
function delProp(prop) {
if (ob.hasOwnProperty(prop)) {
delete ob[prop];
delete target[prop];
render();
} else {
throw new Error(`the property ${prop} is not define`);
}
}
// 渲染函数
function render() {
let html = "";
for (const prop of Object.keys(ob)) {
html += `
<p><span>${prop}</span>: <span>${ob[prop]}</span></p>
`;
}
div.innerHTML = html;
}
// 初始渲染
render();
ob.addProp = addProp;
ob.delProp = delProp;
Object.defineProperties(ob, {
addProp: {
enumerable: false,
},
delProp: {
enumerable: false,
},
});
return ob;
}
// 这个是源对象, obj是用这个对象通过observer函数衍生出来的
const target = {
a: 1,
b: 2,
};
const obj = observer(target);
// 执行操作
console.log(obj.a); // 获取
obj.c = 4; // 修改
obj.addProp("c", 3); // 添加
obj.delProp("c"); // 删除
</script>
</body>
</html>
vue3: proxy实现观察者模式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>proxy实现观察者模式</title>
</head>
<body>
<div id="container"></div>
<script>
function observer(target) {
const div = document.getElementById("container");
const proxy = new Proxy(target, {
set(target, prop, value) {
Reflect.set(target, prop, value);
render();
},
get(target, prop) {
return Reflect.get(target, prop);
},
// 添加删除属性拦截器
deleteProperty(target, prop) {
const result = Reflect.deleteProperty(target, prop);
render();
return result;
},
// 添加定义属性拦截器
defineProperty(target, prop, descriptor) {
const result = Reflect.defineProperty(target, prop, descriptor);
render();
return result;
},
});
function render() {
let html = "";
for (const prop of Object.keys(target)) {
html += `
<p><span>${prop}:</span><span>${target[prop]}</span></p>
`;
}
div.innerHTML = html;
}
render();
return proxy;
}
const target = {
userName: "张三",
gender: "男",
};
const obj = observer(target);
console.log("obj:", obj);
// 执行操作
console.log(obj.age); // 获取
obj.age = 20; // 修改
obj.happy = "打篮球"; // 添加
delete obj.userName; // 删除
</script>
</body>
</html>
最后附上Reflect在MDN上的链接,https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect