# Vue3响应式

+ track() 保存代码
+ effect() 需要运行代码
+ trigger() 运行所有存储的代码

```js
let dep = new Set(); 
let effect = ()=>{total=price * quantity}
function track(){dep.add(effect)}
function trigger(){dep.forEach(effect=>effect())}
```

引入depsMap（一个depsMap对应一个响应式对象），一个dep对应一个响应式对象上的属性

```js
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。存储所有响应式对象）

```js
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``

```js
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

```js
console.log(Reflect.get(Product,'quantity'))
```

```js
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)
}
```

```js
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中读取时才记录

```js
let activeEffect = null
function effect(eff){
    activeEffect = eff
    activeEffect()
    activeEffect = null
}
//..
effect(()=>{
    total = product.price*product.quantity
})
```

```js
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);
   }
}
```

```js
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

```js
salePrice = ref(0)
effect(()=>{
    total = salePrice.value*product.quantity
})
effect(()=>{
    salePrice.value = product.price * 0.9
})
```

*利用reactive*

```js
function ref(intiaValue){
    return reactive({value:intiaValue})
}
```

利用js计算属性

```js
function ref(raw){
    const r = {
        get value(){
            track(r,'value')
            return raw
        }
        set value(newVal){
            raw = newVal; 
            trigger(r,'value')
        }
    }
	return r;
}
```

### computed

```js
function computed(getter){
    let res = ref();
    effect(()=>{
        res.value = getter();
    })
    return res;
}
```

