实现原理描述
1.定一个 reactive 方法 返回 Proxy 实例,get(), set(),
2.通过Reflect.get(target, key) 和 Reflect.set(target, key, value, receiver)抓取属性或设置属性修改
3.增加track trigger监听方法体 订阅响应业务逻辑动作
4.定义订阅发布方法体 effects.js 内部三个方法体(effect(). track(), trigger())
effect() 收集所有动作 track() 获取响应动作。 trigger() 触发响应动作
export const reactive = (target = {}) => {
return new Proxy(target, {
get(target, key, receiver) {
track(target, key);
console.log('get');
return Reflect.get(target, key);
},
set(target, key, value, receiver) {
const res = Reflect.set(target, key, value, receiver); // set 触发
trigger(target, key);
console.log('set');
// target[key] = value
return res
}
});
};
// effect.js
let activeEffect; // 暂存影响方法体
export const effect = (fn) => {
console.log(fn)
const _effect = function () {
activeEffect = _effect;
console.log(activeEffect)
fn();
};
_effect();
};
const targetMap = new WeakMap(); // 定义一个弱映射关系
// 依赖收集
export const track = (target, key) => {
let depsMap = targetMap.get(target); // 获取WeakMap 某个值
if (!depsMap) { // 第一次获取不到 默认填充Map对象
depsMap = new Map(); // 生成一个Map对象 作为目标对象的 值
targetMap.set(target, depsMap); // 原始对象:Map为值 的键值对的方式存入 targetMap中
}
let deps = depsMap.get(key); // 获取原始数据中属性中对应key的值 获取到的是个Set对象
if (!deps) { // 也是获取不到 填充Set
deps = new Set(); // 生成一个Set对象 作为原始对象key对应的值
depsMap.set(key, deps) // deps作为原始对象key对应的值存储
}
deps.add(activeEffect); // 订阅内容 收集所有影响力 添加到 Set 对象中
// 现在的deps 里面存放了 N 个 订阅报文体 里面是
};
// 依赖触发
export const trigger = (target, key) => {
let depsMap = targetMap.get(target);
const deps = depsMap.get(key);
deps.forEach(effect => effect()); // 发布
};
reactive 响应方法使用
<!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>
<script type="module">
import { reactive } from './reactive.js'
import { effect } from './effects.js'
const user = reactive({
name: 'lyon'
})
effect(() => {
// 触发副作用
console.count('副作用触发次数')
console.log(user.name)
document.querySelector('#app').innerText = `丈二和尚:${user.name}`
})
// 2秒后改变数据,页面自动响应变化
setTimeout(() => {
user.name = '响应变化'
console.log(user)
}, 2000);
</script>
<body>
<h1 id="app">222</h1>
</body>
</html>