Vue 3 Pre-Alpha / vue-next 源码学习之 vue/reactivity/baseHandlers.ts

前言

回到之前有一篇《Vue 3 Pre-Alpha / vue-next 源码学习之 vue/reactivity/reactive.ts》我们学习了使用 Proxy 实现的 reactive 构造出来的响应式的对象,定义一个 Proxy 对象是怎么样呢?请看代码: new Proxy(target, handler) 可以看出来,是需要 targethandler 来实现,在本篇中我们就来看看实现这个 handler 的最基础的两种叫 mutableHandlersreadonlyHandlers两者都是基于原生的 ProxyHandler

源码分析

注意带【】符号的为关键代码,关键代码能体现作者的最关键最基础的实现思路

import { reactive, readonly, toRaw } from './reactive'
import { OperationTypes } from './operations' // set、add、delete、clear、get、has、iterate
import { track, trigger } from './effect'
import { LOCKED } from './lock'
import { isObject, hasOwn, isSymbol } from '@vue/shared'
import { isRef } from './ref'

const builtInSymbols = new Set(
  Object.getOwnPropertyNames(Symbol) // ["length", "name", "prototype", "for", "keyFor", "asyncIterator", "hasInstance", "isConcatSpreadable", "iterator", "match", "replace", "search", "species", "split", "toPrimitive", "toStringTag", "unscopables", "matchAll", "useSetter", "useSimple", "observable"]
    .map(key => (Symbol as any)[key])
    .filter(isSymbol) // [Symbol(Symbol.asyncIterator), Symbol(Symbol.hasInstance), Symbol(Symbol.isConcatSpreadable), Symbol(Symbol.iterator), Symbol(Symbol.match), Symbol(Symbol.replace), Symbol(Symbol.search), Symbol(Symbol.species), Symbol(Symbol.split), Symbol(Symbol.toPrimitive), Symbol(Symbol.toStringTag), Symbol(Symbol.unscopables), Symbol(Symbol.matchAll), Symbol(observable)]
)

function createGetter(isReadonly: boolean) { // 针对 OperationTypes 中的 get 进行处理,get 就要 track
  return function get(target: any, key: string | symbol, receiver: any) { // Reflect 与 Proxy 是相辅相成的,在 Proxy 上有的方法,在 Reflect 就一定有
    const res = Reflect.get(target, key, receiver) // 【实现 baseHandlers 的关键代码,使用 Reflect.get】receiver 可以理解为 get 的作用是 receiver,即receiver[key]
    if (isSymbol(key) && builtInSymbols.has(key)) {
      return res
    }
    if (isRef(res)) { // 如果是 Ref 就不需要再 track 了
      return res.value
    }
    track(target, OperationTypes.GET, key) // track get 方法,因此 effect(fn) 的 fn 内的 reactiveEffect / Ref 有 get 的操作将被跟踪,使 effect 有自动响应执行功能
    return isObject(res)
      ? isReadonly
        ? // need to lazy access readonly and reactive here to avoid
          // circular dependency
          readonly(res)
        : reactive(res)
      : res
  }
}

function set(
  target: any,
  key: string | symbol,
  value: any,
  receiver: any
): boolean { // 针对 OperationTypes 中的 set 与 add 进行处理,set / add 就要 trigger
  value = toRaw(value)
  const oldValue = target[key]
  if (isRef(oldValue) && !isRef(value)) {
    oldValue.value = value
    return true
  }
  const hadKey = hasOwn(target, key)
  const result = Reflect.set(target, key, value, receiver) // 【实现 baseHandlers 的关键代码,使用 Reflect.set】receiver 可以理解为 set 的作用是 receiver,看下面 demo
  // Reflect.set demo
  // let person = {
  //   age: 38,
  //   set setAge(value) {
  //     return this.age = value
  //   }
  // }
  // let personReceiver={ // another person
  //   age: 28
  // }
  // let result = Reflect.set(person, 'setAge', 18, personReceiver)
  // console.log(result) // true
  // console.log(person.age) // => 38, 还是 38 不变
  // console.log(personReceiver.age) // => 18, personReceiver 的age 却变了 变为18

  // don't trigger if target is something up in the prototype chain of original
  if (target === toRaw(receiver)) {
    /* istanbul ignore else */
    if (__DEV__) {
      const extraInfo = { oldValue, newValue: value }
      if (!hadKey) {
        trigger(target, OperationTypes.ADD, key, extraInfo)
      } else if (value !== oldValue) {
        trigger(target, OperationTypes.SET, key, extraInfo)
      }
    } else {
      if (!hadKey) {
        trigger(target, OperationTypes.ADD, key)
      } else if (value !== oldValue) {
        trigger(target, OperationTypes.SET, key)
      }
    }
  }
  return result
}

function deleteProperty(target: any, key: string | symbol): boolean { // 针对 OperationTypes 中的 delete 进行处理,delete 也要 trigger
  const hadKey = hasOwn(target, key)
  const oldValue = target[key]
  const result = Reflect.deleteProperty(target, key) // 【实现 baseHandlers 的关键代码,使用 Reflect.deleteProperty】
  if (result && hadKey) {
    /* istanbul ignore else */
    if (__DEV__) {
      trigger(target, OperationTypes.DELETE, key, { oldValue })
    } else {
      trigger(target, OperationTypes.DELETE, key)
    }
  }
  return result
}

function has(target: any, key: string | symbol): boolean { // 针对 OperationTypes 中的 has 进行处理,has 也要 track
  const result = Reflect.has(target, key) // 【实现 baseHandlers 的关键代码,使用 Reflect.has】
  track(target, OperationTypes.HAS, key)
  return result
}

function ownKeys(target: any): (string | number | symbol)[] { // 针对 OperationTypes 中的 iterate 进行处理,iterate 也要 track
  track(target, OperationTypes.ITERATE)
  return Reflect.ownKeys(target) // 【实现 baseHandlers 的关键代码,使用 Reflect.ownKeys 进行遍历】
}

export const mutableHandlers: ProxyHandler<any> = { // mutableHandlers 与下面的 readonlyHandlers 一样,只是一个是只读的
  get: createGetter(false),
  set,
  deleteProperty,
  has,
  ownKeys
}

export const readonlyHandlers: ProxyHandler<any> = { // readonlyToRaw(WeakMap)有值就是 readonly 类的
  get: createGetter(true),

  set(target: any, key: string | symbol, value: any, receiver: any): boolean {
    if (LOCKED) {
      if (__DEV__) {
        console.warn(
          `Set operation on key "${String(key)}" failed: target is readonly.`,
          target
        )
      }
      return true
    } else {
      return set(target, key, value, receiver)
    }
  },

  deleteProperty(target: any, key: string | symbol): boolean {
    if (LOCKED) {
      if (__DEV__) {
        console.warn(
          `Delete operation on key "${String(
            key
          )}" failed: target is readonly.`,
          target
        )
      }
      return true
    } else {
      return deleteProperty(target, key)
    }
  },

  has,
  ownKeys
}

总结

要实现数据的响应式的绑定,实现的思路有很多,如 Vue 2.x 使用的是 Object.definedProperty 的方式。而到了 Vue 3 则大胆使用 ES6 的 Proxyreactive 的实现) 及 get setRef 的实现)。而这里介绍了两种 Vue 3 会用到的 Proxy 的基础 handler(baseHandlers)。下一篇,我们将来介绍另外一种 handler 叫 collectionHandlers。collectionHandler 比 baseHandler 提供了更加丰富的 OperationTypes(set、add、delete、clear、get、has、iterate) 更齐全的操作的监听响应处理。

作者: 博主

Talk is cheap, show me the code!

发表评论

邮箱地址不会被公开。

Captcha Code