Vue3 响应式原理回顾
- Proxy 对象实现属性监听
 - 多层属性嵌套时,在访问属性过程中处理下一级属性
 - 默认监听动态添加的属性
 - 默认监听属性的删除操作
 - 默认监听数组索引和 length 属性
 - 可以作为单独的模块使用
 
Proxy 对象
Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。
严格模式
set 和 deleteProperty 中需要返回布尔类型的值,在严格模式下,如果返回 false 的话会出现 Type Error 的异常;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
   | const target = {     foo: 'foo',     bar: 'bar' }
 
  const proxy = new Proxy(target, {     get (target, key, receiver) {                  return Reflect.get(target, key, receiver)     },     set (target, key, value, receiver) {                  return Reflect.set(target, key, value, receiver)     },     deleteProperty (target, key) {                  return Reflect.deleteProperty(target, key)     } })
  proxy.foo = 'foo123' delete proxy.foo
  | 
receiver
- Proxy 中 receiver:Proxy 或者继承 Proxy 的对象
 - Reflect 中 receiver: 如果 target 对象中设置了 getter,getter 中的 this 指向 receiver
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
   | const obj = {     get foo() {         console.log(this)         return this.bar     } } const proxy = new Proxy(obj, {     get (target, key) {         if (key === 'bar') {             return 'value - bar'         }         return Reflect.get(target, key)     } }) console.log(proxy.foo)
  | 

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
   | const obj = {     get foo() {         console.log(this)         return this.bar     } } const proxy = new Proxy(obj, {     get (target, key, receiver) {         if (key === 'bar') {             return 'value - bar'         }         return Reflect.get(target, key, receiver)     } }) console.log(proxy.foo)
  | 

手写 reactive 函数
reactive
- 接收一个参数,判断参数是否是对象
 - 创建拦截器对象 handler,设置 get / set / deleteProperty
 - 返回 Proxy 对象
 
手写实现
reactive 文件夹下新建 index.js 文件,内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
   | const isObject = val => val !== null && typeof val === 'object' const convert = target => isObject(target) ? reactive(target) : target const hasOwnProperty = Object.prototype.hasOwnProperty const hasOwn = (target, key) => hasOwnProperty.call(target, key)
  export function reactive(target) {     if (!isObject(target)) return     const handler = {         get(target, key, receiver) {                          console.log('get', key)             const result = Reflect.get(target, key, receiver)             return convert(result)         },         set(target, key, value, receiver) {             const oldValue = Reflect.get(target, key, receiver)             let result = true             if (oldValue !== value) {                 result = Reflect.set(target, key, value, receiver)                                  console.log('set', key, value)             }             return result         },         deleteProperty(target, key) {             const hasKey = hasOwn(target, key)             const result = Reflect.deleteProperty(target, key)             if (hasKey && result) {                                  console.log('delete', key)             }             return result         }     }     return new Proxy(target, handler) }
   | 
响应式系统原理 effect && track
在上述 index.js 中继续编写代码:
effect 手写实现
1 2 3 4 5 6 7
   | let activeEffect = null export function effect(callback) {     activeEffect = callback     callback()      activeEffect = null }
 
   | 
track 手写实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14
   | let targetMap = new WeakMap() export function track(target, key) {     if (!activeEffect) return     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) }
   | 
reactive 中收集依赖调用 track
1 2 3 4 5 6 7
   | get(target, key, receiver) {          console.log('get', key)     track(target, key)     const result = Reflect.get(target, key, receiver)     return convert(result) }
  | 
响应式系统原理 trigger
收集依赖结束后,在值修改时,需要触发更新,trigger 函数的作用就在于此
trigger 手写实现
1 2 3 4 5 6 7 8 9 10
   | export function trigger(target, key) {     const depsMap = targetMap.get(target)     if (!depsMap) return     const dep = depsMap.get(key)     if (dep) {         dep.forEach(effect => {             effect()         })     } }
  | 
reactive 中触发更新调用 trigger
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
   | set(target, key, value, receiver) {     const oldValue = Reflect.get(target, key, receiver)     let result = true     if (oldValue !== value) {         result = Reflect.set(target, key, value, receiver)                  trigger(target, key)     }     return result }, deleteProperty(target, key) {     const hasKey = hasOwn(target, key)     const result = Reflect.deleteProperty(target, key)     if (hasKey && result) {                  trigger(target, key)     }     return result }
  | 
响应式系统原理 ref
ref 接收一个原始值或者对象
ref 手写实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
   | export function ref(raw) {          if (isObject(raw) && raw.__v_isRef) return     let value = convert(raw)     const r = {         __v_isRef: true,         get value() {             track(r, 'value')             return value         },         set value(newValue) {             if (newValue !== value) {                 raw = newValue                 value = convert(raw)                 trigger(r, 'value')             }         }     }     return r }
  | 
reactive VS ref
- ref 可以把基本数据类型的数据,转换成响应式对象
 - ref 返回的对象,重新赋值成对象也是响应式的
 - reactive 返回的对象,重新赋值后丢失响应式
 - reactive 返回的对象不可以解构
 
响应式系统原理 toRefs
接收一个对象作为参数,它会遍历对象身上的所有属性,然后挨个设置成响应式数据
toRefs 手写实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
   | export function toRefs(proxy) {     const ret = proxy instanceof Array ? new Array(proxy.length) : {}     for(const key in proxy) {         ret[key] = toProxyRef(proxy, key)     }     return ret }
  function toProxyRef(proxy, key) {     const r = {         __v_isRef: true,         get value() {             return proxy[key]         },         set value(newValue) {             proxy[key] = newValue         }     }     return r }
  | 
响应式系统原理 computed
computed 手写实现
1 2 3 4 5
   | export function computed(getter) {     const res = ref()     effect(() => (res.value = getter()))     return res }
  |