Proxy 对象用于定义基本操作的自定义行为(如属性查找、赋值、枚举、函数调用等)。 对于Proxy对象的任何操作都可以被捕获到,这也是实现双向绑定的基础,而Object.defineProperty()缺没法捕获到对象新增的属性,vue3也使用proxy替代了 defineProperty
<!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>
<input type="range" min='0' max='255' id='r'>
<input type="range" min='0' max='255' id='g'>
<input type="range" min='0' max='255' id='b'>
<div id='color' style="width: 100px;height: 100px;">
</div>
</body>
</html>
<script>
let handlers = new Map();
let reactivities = new Map();
let usedReactivities = []
function reactive(obj) {
//防止多个相同obj的reactive对象
if (reactivities.has(obj)) {
return reactivities.get(obj)
}
let proxy = new Proxy(obj, {
get(obj, prop) {
console.log(obj, prop);
//收集依赖
usedReactivities.push([obj, prop])
//解决a.b.c的情况
if (typeof obj[prop] === 'object') {
return reactive(obj[prop])
}
//一定要return,不然obj[prop]拿不到值
return obj[prop]
},
set(obj, prop, val) {
obj[prop] = val;
// console.log(handlers, 0);
//只执行与prop有关的handler
if (handlers.get(obj)) {
// console.log(handlers.get(obj), 1);
if (handlers.get(obj).get(prop)) {
// console.log(handlers.get(obj).get(prop), 2);
for (const handler of handlers.get(obj).get(prop)) {
handler()
}
}
}
return obj[prop]
}
});
reactivities.set(obj, proxy)
return proxy
};
function effect(handler) {
usedReactivities = [];
//调用时访问属性,proxy就会执行get方法..任何操作proxy对象的行为都会被收集到
handler();
for (const usedReactivity of usedReactivities) {
let [obj, prop] = usedReactivity;
console.log(obj, prop);
if (!handlers.has(obj)) {
handlers.set(obj, new Map())
}
if (!handlers.get(obj).has(prop)) {
handlers.get(obj).set(prop, [])
}
//存放prop的有关操作的执行函数
handlers.get(obj).get(prop).push(handler)
// console.log(handlers.get(obj).get(prop),'prop');
}
}
let p = reactive({ r: 100, g: 255, b: 100 })
effect(() => {
document.getElementById('r').value = p.r
});
effect(() => {
document.getElementById('g').value = p.g
});
effect(() => {
document.getElementById('b').value = p.b
});
document.getElementById('r').addEventListener('input',(event)=>{
p.r=event.target.value
});
document.getElementById('g').addEventListener('input',(event)=>{
p.g=event.target.value
});
document.getElementById('b').addEventListener('input',(event)=>{
p.b=event.target.value
});
effect(()=>{
document.getElementById('color').style.backgroundColor=`rgb({p.r},{p.g},${p.b})`
});
</script>