Vue3.0 响应式系统原理

Vue3 响应式

  • Proxy 对象实现属性监听
  • 多层属性嵌套,在访问属性过程中处理下一级属性
  • 默认监听动态添加的属性
  • 默认监听属性的删除操作
  • 默认监听数组索引和length属性
  • 可以作为单独的模块使用

核心方法

  • reactive/ref/toRefs/computed
  • effect
  • track
  • trigger

Proxy

// 问题1: set 和 deleteProperty 中需要返回布尔类型的值
    //        在严格模式下,如果返回 false 的话会出现 Type Error 的异常
    const target = {
      foo: 'xxx',
      bar: 'yyy'
    }
    // Reflect.getPrototypeOf()
    // Object.getPrototypeOf()
    const proxy = new Proxy(target, {
      get (target, key, receiver) {
        // return target[key]
        return Reflect.get(target, key, receiver)
      },
      set (target, key, value, receiver) {
        // target[key] = value
        return Reflect.set(target, key, value, receiver)
      },
      deleteProperty (target, key) {
        // delete target[key]
        return Reflect.deleteProperty(target, key)
      }
    })

    proxy.foo = 'zzz'
    // delete proxy.foo


    // 问题2:Proxy 和 Reflect 中使用的 receiver

    // Proxy 中 receiver:Proxy 或者继承 Proxy 的对象
    // Reflect 中 receiver:如果 target 对象中设置了 getter,getter 中的 this 指向 receiver

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

reactive

  • 接收一个参数,判断参数是否是对象
  • 创建拦截器对象 handler,设置 get/set/deleteProperty
  • 返回 Proxy 对象
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) {
  //1.判断是否是对象
  if (!isObject(target)) return target

  const handler = {
    get(target, key, receiver) {
      //收集依赖
      const res = Reflect.get(target, key, receiver)
      return convert(res)
    },
    set(target, key, value, receiver) {
      const oldval = Reflect.get(target, key, receiver)
      let result = true
      if (oldval !== value) {
        result = Reflect.set(target, key, value, receiver)
        //触发更新

      }
      return result //返回布尔值结果
    },
    deleteProperty(target, key) {
      const haskey = hasOwn(target, key)
      const result = Reflect.deleteProperty(target, key)
      if (haskey && result) {
        //触发更新

      }
      return result //返回布尔值结果
    }
  }

  return new Proxy(target, handler)
}

收集依赖 effect && track

export function effect(callback) {
  activeEffect = callback
  callback() //访问响应式对象属性,去收集依赖
  activeEffect = null
}

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

trigger

//触发更新
export function trigger(target, key) {
  const depsMap = targetMap.get(target)
  if (!depsMap) return
  const dep = depsMap.get(key)
  if (dep) {
    dep.forEach(effect => {
      effect()
    });
  }
}

创建响应式对象 ref

export function ref(raw) {
  //判断 raw是否是ref创建的对象  是直接返回
  if (isObject(raw) && raw.__v_isRef) return

  let value = convert(raw) //判断是否是对象 是的话 创建reactive 响应式对象

  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')
      }
    }
  }
}

reactive ref

  • ref 可以把基本数据类型数据,转成响应式对象
  • ref 返回的对象,重新赋值成对象也是响应式的
  • reactive 返回的对象,重新赋值丢失响应式
  • reactive 返回的对象不可以解构

toRefs函数

接收一个reactive返回的响应式对象,也就是一个proxy对象,传入参数不是reactive创建的响应式对象直接返回,再把传入对象的所有属性转换成一个类似ref返回的对象,把转换后的属性挂载在一个新的对象上返回。

function toProxyRef(proxy, key) {
  const r = {
    __v_isRef: true,
    get value() {
      return proxy[key]
    },
    set value(newValue) {
      proxy[key] = newValue
    }
  }
  return r
}

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
}

computed

需要接收一个有返回值的函数作为参数,这个函数的返回值就是计算属性的值,并且需要监听这个函数内部使用响应数据的变化,最后将这个函数执行的结果返回。

export function computed(getter){
  const result = ref()

  effect( () => ( result.value = getter() ) )
  
  return result
}

所有方法


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) {
  //1.判断是否是对象
  if (!isObject(target)) return target

  const handler = {
    get(target, key, receiver) {
      //收集依赖
      track(target, key)
      const res = Reflect.get(target, key, receiver)
      return convert(res)
    },
    set(target, key, value, receiver) {
      const oldval = Reflect.get(target, key, receiver)
      let result = true
      if (oldval !== 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 //返回布尔值结果
    }
  }

  return new Proxy(target, handler)
}

let activeEffect = null

export function effect(callback) {
  activeEffect = callback
  callback() //访问响应式对象属性,去收集依赖
  activeEffect = null
}

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

//触发更新
export function trigger(target, key) {
  const depsMap = targetMap.get(target)
  if (!depsMap) return
  const dep = depsMap.get(key)
  if (dep) {
    dep.forEach(effect => {
      effect()
    });
  }
}

export function ref(raw) {
  //判断 raw是否是ref创建的对象  是直接返回
  if (isObject(raw) && raw.__v_isRef) return

  let value = convert(raw) //判断是否是对象 是的话 创建reactive 响应式对象

  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')
      }
    }
  }
}

function toProxyRef(proxy, key) {
  const r = {
    __v_isRef: true,
    get value() {
      return proxy[key]
    },
    set value(newValue) {
      proxy[key] = newValue
    }
  }
  return r
}

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
}

export function computed(getter) {
  const result = ref()

  effect(() => (result.value = getter()))

  return result
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容