V3-reactive 定义响应式的实现(订阅-发布模式)

84 阅读2分钟

实现原理描述

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() 触发响应动作

image.png

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>