Vue3响应式简单实现

本文介绍了如何在JavaScript中实现响应式编程,通过使用副作用函数、依赖管理和Proxy对象来实现实时更新。重点讲解了effect、依赖追踪以及ref的创建和使用,通过一个简单的输入框示例展示了响应式编程的实际应用。
摘要由CSDN通过智能技术生成

什么是响应式?

通俗来讲,就是数据更新时,视图也实时更新。在JavaScript中,无法实现这样的效果

let A0 = 1;
let A1 = 2;
let A2 = A0 + A1;

console.log(A2) // 3

A0 = 0

console.log(A2) // 3

如果我们想要实现响应式的效果,就要将其包装为函数

const update = () => {
    A2 = A0 + A1
}

根据官方文档定义几个术语:

  • 这个update函数会产生一个副作用,简称为作用(effect),因为它会更改程序里的状态
  • A0 和 A1 被视为这个作用的依赖 (dependency),因为它们的值被用来执行这个作用。因此这次作用也可以说是一个它依赖的订阅者 (subscriber)。

此时,update有几个任务:

  • 当一个变量被读取时进行追踪。例如我们执行了表达式 A0 + A1 的计算,则 A0 和 A1 都被读取到了。
  • 如果一个变量在当前运行的副作用中被读取了,就将该副作用设为此变量的一个订阅者。例如由于 A0 和 A1 在 update() 执行时被访问到了,则 update() 需要在第一次调用之后成为 A0 和 A1 的订阅者。

  • 探测一个变量的变化。例如当我们给 A0 赋了一个新的值后,应该通知其所有订阅了的副作用重新执行。

实现简单响应式

1.收集和更新依赖

// 实现effect副作用函数
let activeEffect;
const effect = (fn:Function) => {
    const _effect = function () {
        activeEffect = _effect
        fn()
    }
    _effect()
}


// 收集依赖
let targetMap new WeakMap();
const track = (target, key) => {
    let depsMap = targetMap(target)
    if (!depsMap) {
        depsMap = new Map()
        targetMap.set(target, depsMap)
    }
    let deps = depsMap.get(key)
    if (!deps) {
        deps = new Set()
        depsMap.set(key, deps)
    }

    deps.add(activeEffect)
}

// 更新依赖
const trigger = (target, key) => {
    let depsMap = target.get(target)
    if (!despMap) return ;
    let deps = depsMap.get(key)
    
    deps.forEach(effect=>effect())
}

2.实现ref

const reactive = <T extends Object>(target:T) => {
    return new Proxy(target, {
        get (target, key, receiver) {
            track(target, key)
            return Reflect.get(target, key)
        },
        set (target, key, value, recevier) {
            let res = Reflect.set(target, key, value)
            trigger(target, key)
            return res
        }
    })
}

简易demo

输入框输入,视图实时更新

<!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>
	<body>
		<input type="text" />
		<h1></h1>
		<script>
			let activeEffect;
			/**
			 * 副作用函数
			 * 接受一个匿名函数
			 */
			const effect = fn => {
				const _effect = function () {
					activeEffect = _effect;
					fn();
				};

				_effect();
			};

			/**
			 * 收集依赖
			 */
			const targetMap = new WeakMap();
			const track = (target, key) => {
				let depsMap = targetMap.get(target);
				if (!depsMap) {
					depsMap = new Map();
					targetMap.set(target, depsMap);
				}

				let desp = depsMap.get(key);
				if (!desp) {
					desp = new Set();
					depsMap.set(key, desp);
				}

				desp.add(activeEffect);
			};

			/**
			 * 更新依赖
			 */
			const trigger = (target, key) => {
				const depsMap = targetMap.get(target);
				// if (!depsMap) return;
				const deps = depsMap.get(key);

				deps.forEach(effect => effect());
			};

			/**
			 * Proxy响应式
			 * target: 要代理的对象
			 */
			const ref = target => {
				return new Proxy(target, {
					get(target, key, receiver) {
						console.log('get   ' + target[key]);
						track(target, key);
						return Reflect.get(target, key, receiver);
					},
					set(target, key, value, receiver) {
						console.log('set   ' + target[key]);
						let res = Reflect.set(target, key, value, receiver);
						trigger(target, key);
						return res;
					},
				});
			};

			const user = ref({
				name: '21',
				age: 23,
			});

			const input = document.querySelector('input');
			const h1 = document.querySelector('h1');

			const change = e => {
				user.name = e.target.value;
				console.log('user.name::: ', user.name);
			};

			input.addEventListener('input', change);

			effect(() => {
				h1.innerText = `${user.name} --- ${user.age}`;
			});
		</script>
	</body>
</html>

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值