什么是响应式?
通俗来讲,就是数据更新时,视图也实时更新。在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>