Vue3响应式
- track() 保存代码
- effect() 需要运行代码
- trigger() 运行所有存储的代码
let dep = new Set();
let effect = ()=>{total=price * quantity}
function track(){dep.add(effect)}
function trigger(){dep.forEach(effect=>effect())}
引入depsMap(一个depsMap对应一个响应式对象),一个dep对应一个响应式对象上的属性
const depsMap = new Map();
function track(key){
let dep = depsMap.get(key);
if(!dep){
depsMap.set(key,(dep = new Set()));
}
dep.add(effect);
}
function trigger(key){
let dep = depsMap.get(key);
if(dep){
dep.forEach(effect=>effect())
}
}
targetMap(weekMap,key是object。存储所有响应式对象)
const targetMap = new WeakMap();
function track(target,key){
let depsMap = targetMap.get(target);
if(!depsMap){
targetMap.set(target,(depsMap = new Map()));
}
let dep = depsMap.get(key);
if(!dep){
depsMap.set(key,(dep = new Set()));
}
dep.add(effect);
}
function trigger(target,key){
let depsMap = targetMap.get(target);
if(!depsMap){return}
let dep = depsMap.get(key);
if(dep){
dep.forEach(effect=>effect())
}
}
test
let product = {price:5,quantity:2}
let total = 0;
let effect = ()=>{
total = product.price*product.quantity;
}
track(product,'quantity');
effect();
/*
> total //10
> trigger(product,'quantity')
> total //15
*/
question:如何自动执行?
响应式引擎
当我们运行effect,如果访问(get)了产品的属性,我们想track去保存effect,当我们(set)去改变产品的属性时,我们想用trigger去运行保存的effect
怎样去拦截GET,SET?(Proxy、reactive)
- vue2 使用了ES5 的Object.defineProperty()
- vue3 使用ES6 的Reflect 和Proxy
console.log(Reflect.get(Product,'quantity'))
function reactive(target){
const handler = {
get(target,key,receiver){
let res = Reflect.get(target,key,receiver)//receiver能保证this指向正确
track(target,key);
return res;
},
set(target,key,value,receiver){
let oldValue = target[key];
let res = Reflect.set(target,key,value,receiver);
if(oldValue!=res){
trigger(target,key);
}
return res
}
}
return new Proxy(target,handler)
}
let product = reactive({price:5,quantity:2})
let total = 0;
let effect = ()=>{
total = product.price*product.quantity;
}
effect();
console.log(total);
product.quantity = 3;
console.log(total)
是否需要在所有地方的get上调用track?
activeEffect(优化)
仅在effect中读取时才记录
let activeEffect = null
function effect(eff){
activeEffect = eff
activeEffect()
activeEffect = null
}
//..
effect(()=>{
total = product.price*product.quantity
})
function track(target,key){
if(activeEffect){
let depsMap = targetMap.get(target);
if(!depsMap){
targetMap.set(target,(depsMap = new Map()));
}
let dep = depsMap.get(key);
if(!dep){
depsMap.set(key,(dep = new Set()));
}
dep.add(activeEffect);
}
}
let product = reactive({price:5,quantity:2})
let salePrice = 0;
let total = 0;
effect(()=>{
total = product.price*product.quantity
})
effect(()=>{
salePrice = product.price * 0.9
})
console.log(`before update total = ${total} salePrice = ${salePrice}`)
product.quantity = 3;
console.log(`after update total = ${total} salePrice = ${salePrice}`)
product.price = 10
console.log(`after update total = ${total} salePrice = ${salePrice}`)
把product.price换成salePrice怎么实现响应式呢?
Ref
salePrice = ref(0)
effect(()=>{
total = salePrice.value*product.quantity
})
effect(()=>{
salePrice.value = product.price * 0.9
})
利用reactive
function ref(intiaValue){
return reactive({value:intiaValue})
}
利用js计算属性
function ref(raw){
const r = {
get value(){
track(r,'value')
return raw
}
set value(newVal){
raw = newVal;
trigger(r,'value')
}
}
return r;
}
computed
function computed(getter){
let res = ref();
effect(()=>{
res.value = getter();
})
return res;
}