前言
vue3响应式原理加api编写,快速明白vue3响应式原理
GitHub博客:https://github.com/jiejiangzi/blog/issues/8
vue3响应式原理实现
先写一段代码看下
实现effect
var name = 'sl', age = 22;
effect1 = () => `我叫${name},今年${age}岁`
effect2 = () => `我叫${name},今年${age+1}岁`
console.log(effect1()) //我叫sl,今年22岁
console.log(effect2()) //我叫sl,今年23岁
age = 30;
console.log(effect1()) //我叫sl,今年30岁
console.log(effect2()) //我叫sl,今年31岁
看看有什么可以优化的点呢?
首先:多个函数,在age发生变化后需要手动再次调用多个函数才可以获取最新信息
期望可以修改信息以后自动调用多个函数
如何实现呢
可以想到将多个函数存放到一起存放到gather函数,并且让age发生变化时可以将多个函数调用trigger调用
实现gather及trigger
var name = "sl",
age = 22;
var tom, joy;
effect1 = () => (tom = `我叫${name},今年${age}岁`);
effect2 = () => (joy = `我叫${name},今年${age + 1}岁`);
var dep = new Set();
function gather() {
dep.add(effect1);
dep.add(effect2);
}
function trigger() {
dep.forEach((effect) => effect());
}
gather();
effect1()
effect2()
console.log(tom); //我叫sl,今年22岁
console.log(joy); //我叫sl,今年23岁
age = 30;
trigger()
console.log(tom); //我叫sl,今年30岁
console.log(joy); //我叫sl,今年31岁
再继续看下还是有什么可以优化的点
如果变量是一个对象或多个对象的话该怎么处理呢
变量为原始类型时Set存储
变量为对象时可以用map存储
多个对象时用weakMap存储
var obj1 = { name: "tom", age: 22 };
var obj2 = { name: "joy", age: 23 };
var tom, joy;
effect1 = () => (tom = `我叫${obj1.name},今年${obj1.age}岁`);
effect2 = () => (joy = `我叫${obj2.name},今年${obj2.age}岁`);
var depsMap = new WeakMap();
function gather(target, key) {
let depMap = depsMap.get(target);
if (!depMap) {
depsMap.set(target, (depMap = new Map()));
}
let dep = depMap.get(key);
if (!dep) {
depMap.set(key, (dep = new Set()));
}
if (target === obj1) {
dep.add(effect1);
} else {
dep.add(effect2);
}
}
function trigger(target, key) {
let depMap = depsMap.get(target);
if (depMap) {
const dep = depMap.get(key);
if (dep) {
dep.forEach((effect) => effect());
}
}
}
gather(obj1, "age");//收集依赖
gather(obj2, "age");//收集依赖
effect1();
effect2();
console.log(tom); //我叫sl,今年22岁
console.log(joy); //我叫sl,今年23岁
obj1.age = 30;
obj2.age = 10;
trigger(obj1, "age");
trigger(obj2, "age");
console.log(tom); //我叫sl,今年30岁
console.log(joy); //我叫sl,今年31岁
在继续看看有哪些可以优化的点
上边依赖的收集gather以及函数的更新通知trigger每次都是手动收集手动触发更新,那有什么方法可以自动收集及触发吗
Proxy
实现reactive
先写一个reactive函数
function reactive(target) {
const handle = {
set(target, key, value, receiver) {
Reflect.set(target, key, value, receiver);
trigger(receiver,key) // 设置值时触发自动更新
},
get(target, key, receiver) {
gather(receiver, key); // 访问时收集依赖
return Reflect.get(target, key, receiver);
},
};
return new Proxy(target, handle);
}
然后将reactive函数应用到之前代码
var obj1 = reactive({ name: "tom", age: 22 });
var obj2 = reactive({ name: "joy", age: 23 });
var tom, joy;
effect1 = () => (tom = `我叫${obj1.name},今年${obj1.age}岁`);
effect2 = () => (joy = `我叫${obj2.name},今年${obj2.age}岁`);
var depsMap = new WeakMap();
function gather(target, key) {
let depMap = depsMap.get(target);
if (!depMap) {
depsMap.set(target, (depMap = new Map()));
}
let dep = depMap.get(key);
if (!dep) {
depMap.set(key, (dep = new Set()));
}
if (target === obj1) {
dep.add(effect1);
} else {
dep.add(effect2);
}
}
function trigger(target, key) {
let depMap = depsMap.get(target);
if (depMap) {
const dep = depMap.get(key);
if (dep) {
dep.forEach((effect) => effect());
}
}
}
effect1();
effect2();
console.log(tom); //我叫sl,今年22岁
console.log(joy); //我叫sl,今年23岁
obj1.age = 30;
obj2.age = 10;
console.log(tom); //我叫sl,今年30岁
console.log(joy); //我叫sl,今年31岁
然后还有个问题,就是gather函数中有写死dep添加函数
如何解决呢 重写effect函数
let activeEffect = null
function effect(fn) {
activeEffect = fn;
activeEffect();
activeEffect = null; // 执行后立马变成null
}
var depsMap = new WeakMap();
function gather(target, key) {
// 避免例如console.log(obj1.name)而触发gather
if (!activeEffect) return;
let depMap = depsMap.get(target);
if (!depMap) {
depsMap.set(target, (depMap = new Map()));
}
let dep = depMap.get(key);
if (!dep) {
depMap.set(key, (dep = new Set()));
}
dep.add(activeEffect) //将函数添加到依赖
}
effect(effect1);
effect(effect2);
reactive也已经实现了,那么还有ref也实现下
ref
在vue3中ref怎么使用呢
var name = ref('tom')
console.log(name.value) // tom
需要使用.value的方式获取值
function ref(name){
return reactive(
{
value: name
}
)
}
const name = ref('tom');
console.log(name.value) //tom
完整代码
var activeEffect = null;
function effect(fn) {
activeEffect = fn;
activeEffect();
activeEffect = null;
}
var depsMap = new WeakMap();
function gather(target, key) {
// 避免例如console.log(obj1.name)而触发gather
if (!activeEffect) return;
let depMap = depsMap.get(target);
if (!depMap) {
depsMap.set(target, (depMap = new Map()));
}
let dep = depMap.get(key);
if (!dep) {
depMap.set(key, (dep = new Set()));
}
dep.add(activeEffect)
}
function trigger(target, key) {
let depMap = depsMap.get(target);
if (depMap) {
const dep = depMap.get(key);
if (dep) {
dep.forEach((effect) => effect());
}
}
}
function reactive(target) {
const handle = {
set(target, key, value, receiver) {
Reflect.set(target, key, value, receiver);
trigger(receiver, key); // 设置值时触发自动更新
},
get(target, key, receiver) {
gather(receiver, key); // 访问时收集依赖
return Reflect.get(target, key, receiver);
},
};
return new Proxy(target, handle);
}
function ref(name){
return reactive(
{
value: name
}
)
}
以上就是解析vue3响应式原理以及api编写的详细内容,转载自php中文网
发表评论 取消回复