给vue3源码添加注释:数据响应式部分

项目地址

vue 3.0,版本Pre-Alphavue

数据响应源码地址

packages->reactivity->src->reactive.tsreact

引入的代码加注释

isObject

export const isObject = (val: any): val is Record<any, any> => val !== null && typeof val === 'object'
// 引入packages->shared 文件夹下的isObject函数
// 类型保护, al is Record 类型谓词,每当调用isObject这个函数的时候,传入任意的值必须是Record这个函数里的一个参数名,这个函数定义了任意的泛型,返回的是一个是不是对象的布尔值判断,而且不能为空
复制代码

toTypeString

export const objectToString = Object.prototype.toString
export const toTypeString = (value: unknown): string => objectToString.call(value)
// 引入packages->shared 文件夹下的toTypeString函数
// 类型判断,调用toTypeString这个函数的时候,传入一个安全的任意类型(unknown),而且必定要是字符串,而后经过Object.prototype.toString.call()的方式判断究竟是不是字符串
// unknown 和 any 的主要区别是 unknown 类型会更加严格:在对 unknown 类型的值执行大多数操做以前,咱们必须进行某种形式的检查。而在对 any 类型的值执行操做以前,咱们没必要进行任何检查。
// (value: unknown): string 多是表示参数必须是unknown类型,返回值是string类型
复制代码

builtInSymbols

const builtInSymbols = new Set(
  Object.getOwnPropertyNames(Symbol)
    .map(key => (Symbol as any)[key])
    .filter(value => typeof value === 'symbol')
)
// 引入 packages/reactivity/src/baseHandlers.ts 中的builtInSymbols
// 定义了一个Set集合类
// Object.getOwnPropertyNames(Symbol) 返回symbol全部的可枚举和不可枚举属性名称字符串
// map(key => (Symbol as any)[key]) 转换成js是 map(function (key) { return Symbol[key]; }), as any 的目的是为了防止 TS 编译器抱怨,由于从类型上 key 和 Symbol 无关联,你直接拿 key 访问会让 TS 惧怕访问到不存在的属性
// filter(value => typeof value === 'symbol') 筛选出值的类型是symbol的数据
复制代码

JS 内置了一些特殊的 Symbol(好比迭代器),表现上也至关于一个对象的属性,但这种属性是不能被收集响应依赖的,因此要排除掉它。这个代码的主要做用是肯定哪些属性(Symbol)符合这个状况(从 Symbol 上获取类型为 Symbol 的属性)git

isRef

export const refSymbol = Symbol(__DEV__ ? 'refSymbol' : undefined)
export function isRef(v: any): v is Ref<any> {
  return v ? v[refSymbol] === true : false
}
// 引入 packages/reactivity/src/ref.ts 中的isRef
// 类型保护,传入数据的属性必需要是 refSymbol 的 Symbol 才为true,不然为false
复制代码

track

export const enum OperationTypes {
  // 使用文字字符串替换数字,这样更容易检查
  // debugger events
  SET = 'set',
  ADD = 'add',
  DELETE = 'delete',
  CLEAR = 'clear',
  GET = 'get',
  HAS = 'has',
  ITERATE = 'iterate'
}
// WeakMap弱引用的map对象,这里主要是为了不内存泄漏
const targetMap = new WeakMap<any, KeyToDepMap>()

///////////////////上面来自其余文件的引用/////////////////////

export interface ReactiveEffect {
  (): any
  isEffect: true
  active: boolean
  raw: Function
  deps: Array<Dep>
  computed?: boolean
  scheduler?: (run: Function) => void
  onTrack?: (event: DebuggerEvent) => void
  onTrigger?: (event: DebuggerEvent) => void
  onStop?: () => void
}
// 规定activeReactiveEffectStack必须听从上面的ReactiveEffect接口
export const activeReactiveEffectStack: ReactiveEffect[] = []

export function track( target: any, type: OperationTypes, key?: string | symbol ) {
  // 若是没被跟踪就不执行
  if (!shouldTrack) {
    return
  }
  // 获取追踪到的属性
  const effect = activeReactiveEffectStack[activeReactiveEffectStack.length - 1]
  // 若是有值就执行操做
  if (effect) {
    // 若是类型是迭代器,那么key设置成迭代器的Symbol
    if (type === OperationTypes.ITERATE) {
      key = ITERATE_KEY
    }
    // 获取存储的值
    let depsMap = targetMap.get(target)
    // 若是没有,就存贮,void 0 就是 undfined,防止 undfined 被重写
    if (depsMap === void 0) {
      targetMap.set(target, (depsMap = new Map()))
    }
    // 获取key,传入的key值不能为空,在ts中属性或者参数后面加感叹号是不能为空的意思
    let dep = depsMap.get(key!)
    // 若是没有,就存贮
    if (dep === void 0) {
      depsMap.set(key!, (dep = new Set()))
    }
    // 若是在存贮中找不到effect,就添加,而且在effect的deps属性中添加dep
    if (!dep.has(effect)) {
      dep.add(effect)
      effect.deps.push(dep)
      if (__DEV__ && effect.onTrack) {
        effect.onTrack({
          effect,
          target,
          type,
          key
        })
      }
    }
  }
}
复制代码

这个函数的主要做用是深刻追踪对象中的内层而且把追踪到的数据存贮起来es6

createGetter

function createGetter(isReadonly: boolean) {
  return function get(target: any, key: string | symbol, receiver: any) {
    const res = Reflect.get(target, key, receiver)
    if (typeof key === 'symbol' && builtInSymbols.has(key)) {
      return res
    }
    if (isRef(res)) {
      return res.value
    }
    // 经过track函数作深度追踪处理
    track(target, OperationTypes.GET, key)
    return isObject(res)
      ? isReadonly
        ? // need to lazy access readonly and reactive here to avoid
          // circular dependency
          // 须要以只读和延迟反应的方式去避免循环依赖
          readonly(res)
        : reactive(res)
      : res
  }
}
// 引入 packages/reactivity/src/baseHandlers.ts 中的createGetter函数
// 建立一个函数,传入一个布尔值,拦截数据某个属性的读取操做
// target 目标对象
// key 属性名
// receiver 属性名和 proxy 实例自己(严格地说,是操做行为所针对的对象)
// Reflect es6 原生api, Reflect对象的方法与Proxy对象的方法一一对应,只要是Proxy对象的方法,就能在Reflect对象上找到对应的方法
// 上面代码中,每个Proxy对象的拦截操做(get),内部都调用对应的Reflect方法,目的是为了保证原生行为可以正常执行
复制代码

这个函数的主要做用是拦截数据的get操做,经过Reflect返回的深层数据作进一步的处理,判断是不是对象,是对象的话从新走一遍循环github

mutableHandlers

export const mutableHandlers: ProxyHandler<any> = {
  get: createGetter(false),
  set,
  deleteProperty,
  has,
  ownKeys
}
// 引入 packages/reactivity/src/baseHandlers.ts 中的mutableHandlers
// 类型断言,定义一些是proxy的handler,标记一个<any>的泛型,编译器这里会提供ProxyHandler的代码属性提示
复制代码

若是数据是可变的,就执行这个api

readonlyHandlers

export const readonlyHandlers: ProxyHandler<any> = {
  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
}
复制代码

这个的做用很简单的了,若是你定义的数据是只读的,那么若是你想要改变这个数据的时候,就会给你警告,failed: target is readonly数组

hasOwn

const hasOwnProperty = Object.prototype.hasOwnProperty
export const hasOwn = (
  val: object,
  key: string | symbol
): key is keyof typeof val => hasOwnProperty.call(val, key)
// hasOwnProperty() 方法会返回一个布尔值,指示对象自身属性中是否具备指定的属性(也就是,是否有指定的键)
// is ts中有一个特殊的关键字,能够用来判断一个变量属于某个接口|类型
// 这个方法是解决 数组push时,会调用两次 set 的状况,好比 arr.push(1)
// 第一次set,在数组尾部添加1
// 第二次set,给数组添加length属性
复制代码

createInstrumentationGetter

function createInstrumentationGetter(instrumentations: any) {
  return function getInstrumented( target: any, key: string | symbol, receiver: any ) {
    // 判断key是不是instrumentations自己的属性,而且target包含key,就使用instrumentations,不然就使用target
    // key in target in 在这里是作判断用, key 是否在 target中
    // 当“target”为数组时,“key”指的是数组的“索引”;当“target”为对象是,“key”指的是对象的“属性”。
    target =
      hasOwn(instrumentations, key) && key in target ? instrumentations : target
    return Reflect.get(target, key, receiver)
  }
}
复制代码

柯里化函数,初次调用返回getInstrumented函数,getInstrumented函数的做用是,避免屡次proxy的重复操做安全

mutableCollectionHandlers

export const mutableCollectionHandlers: ProxyHandler<any> = {
  get: createInstrumentationGetter(mutableInstrumentations)
}
// 可变数据处理函数
复制代码

readonlyCollectionHandlers

export const readonlyCollectionHandlers: ProxyHandler<any> = {
  get: createInstrumentationGetter(readonlyInstrumentations)
}
// 只读数据处理函数
复制代码

UnwrapNestedRefs

export interface Ref<T> {
  [refSymbol]: true
  value: UnwrapNestedRefs<T>
}
export type UnwrapNestedRefs<T> = T extends Ref<any> ? T : UnwrapRef<T>
// T extends Ref<any> 条件类型,若是 T 可以赋值给 Ref 那么就用T,不然用UnwrapRef<T>
复制代码

做用是遇到 Ref 嵌套 Ref 时能正确(递归)推导出业务数据的类型服务器

UnwrapRef

// Recursively unwraps nested value bindings.
// 递归打开嵌套的绑定值
export type UnwrapRef<T> = {
  ref: T extends Ref<infer V> ? UnwrapRef<V> : T
  array: T extends Array<infer V> ? Array<UnwrapRef<V>> : T
  object: { [K in keyof T]: UnwrapRef<T[K]> }
  stop: T
}[T extends Ref<any>
  ? 'ref'
  : T extends Array<any>
    ? 'array'
    : T extends BailTypes
      ? 'stop' // bail out on types that shouldn't be unwrapped // 在不该该打开的类型上退出
      : T extends object ? 'object' : 'stop']
// T extends Ref<infer V> infer 前缀表示须要返回的类型,T extends Ref<infer V> 表示 T 能够赋值给 Ref 的返回类型
复制代码

ReactiveEffect

export interface ReactiveEffect {
  (): any
  isEffect: true
  active: boolean
  raw: Function
  deps: Array<Dep>
  computed?: boolean
  scheduler?: (run: Function) => void
  onTrack?: (event: DebuggerEvent) => void
  onTrigger?: (event: DebuggerEvent) => void
  onStop?: () => void
}
// 定义的接口
复制代码

源码加注释

import { isObject, toTypeString } from '@vue/shared'
import { mutableHandlers, readonlyHandlers } from './baseHandlers'

import {
  mutableCollectionHandlers,
  readonlyCollectionHandlers
} from './collectionHandlers'

import { UnwrapNestedRefs } from './ref'
import { ReactiveEffect } from './effect'

// The main WeakMap that stores {target -> key -> dep} connections.
// Conceptually, it's easier to think of a dependency as a Dep class
// which maintains a Set of subscribers, but we simply store them as
// raw Sets to reduce memory overhead.
// 从概念上讲,将依赖项看做维护一组订阅服务器的dep类比较容易,但咱们只是将它们存储为原始Set以减小内存开销。
export type Dep = Set<ReactiveEffect>
export type KeyToDepMap = Map<string | symbol, Dep>
export const targetMap = new WeakMap<any, KeyToDepMap>()

// WeakMaps that store {raw <-> observed} pairs.
// WeakMap 存贮 {raw <-> observed} 的映射
// WeakMap弱引用的map对象,这里主要是为了不内存泄漏
const rawToReactive = new WeakMap<any, any>()
// 存贮可变的
const reactiveToRaw = new WeakMap<any, any>()
const rawToReadonly = new WeakMap<any, any>()
// 存贮只读的
const readonlyToRaw = new WeakMap<any, any>()

// WeakSets for values that are marked readonly or non-reactive during
// observable creation.
// WeakSet 用来建立只读或者非响应对象的标记
// WeakSet 弱引用Set ,不能存贮值,只能存贮对象引用,主要也是为了不内存泄漏
const readonlyValues = new WeakSet<any>()
const nonReactiveValues = new WeakSet<any>()

// 全部存贮类型的集合
const collectionTypes = new Set<Function>([Set, Map, WeakMap, WeakSet])
// 设置白名单正则
const observableValueRE = /^\[object (?:Object|Array|Map|Set|WeakMap|WeakSet)\]$/

// 开始检测那些值是能够被proxy
const canObserve = (value: any): boolean => {
  return (
    // 不是vue 对象
    !value._isVue &&
    // 不是 vNode
    !value._isVNode &&
    // 白名单: Object|Array|Map|Set|WeakMap|WeakSet
    observableValueRE.test(toTypeString(value)) &&
    // 没有代理过的
    !nonReactiveValues.has(value)
  )
}

export function reactive<T extends object>(target: T): UnwrapNestedRefs<T> export function reactive(target: object) {
  // if trying to observe a readonly proxy, return the readonly version.
  // 若是监测到proxy是只读的,就返回只读的版本
  if (readonlyToRaw.has(target)) {
    return target
  }
  // target is explicitly marked as readonly by user
  // 若是target被用户设置为只读,则让它只读,并返回
  if (readonlyValues.has(target)) {
    return readonly(target)
  }
  return createReactiveObject(
    target,
    // 如下是上面代码设置的一堆WeakMap
    rawToReactive,
    reactiveToRaw,
    mutableHandlers,
    mutableCollectionHandlers
  )
}

export function readonly<T extends object>( target: T ): Readonly<UnwrapNestedRefs<T>> export function readonly(target: object) {
  // value is a mutable observable, retrieve its original and return
  // a readonly version.
  // 若是监测到值是可改变的,就找出原始值并返回只读版本
  if (reactiveToRaw.has(target)) {
    target = reactiveToRaw.get(target)
  }
  return createReactiveObject(
    target,
    rawToReadonly,
    readonlyToRaw,
    readonlyHandlers,
    readonlyCollectionHandlers
  )
}

// 建立响应式对象
function createReactiveObject( target: any, toProxy: WeakMap<any, any>, toRaw: WeakMap<any, any>, baseHandlers: ProxyHandler<any>, collectionHandlers: ProxyHandler<any> ) {
  // 判断target类型,是不是对象,不是的话就发送警告
  if (!isObject(target)) {
    if (__DEV__) {
      console.warn(`value cannot be made reactive: ${String(target)}`)
    }
    return target
  }
  // target already has corresponding Proxy
  // 已经Proxy处理过的,不须要再次处理
  let observed = toProxy.get(target)
  // 不为空的状况下返回值,void 0 就是 undfined,防止 undfined 被重写
  if (observed !== void 0) {
    return observed
  }
  // target is already a Proxy
  // target 已是Proxy
  if (toRaw.has(target)) {
    return target
  }
  // only a whitelist of value types can be observed.
  // 只有加入白名单的对象才能被代理
  if (!canObserve(target)) {
    return target
  }
  // 若是是已经处理过,而且存贮为 Set, Map, WeakMap, WeakSet 类型的值,那么使用collectionHandlers 集合处理程序,不然使用baseHandlers 基本处理程序
  const handlers = collectionTypes.has(target.constructor)
    ? collectionHandlers
    : baseHandlers
  // 这里整个数据响应式最总要的一行代码,new Proxy
  observed = new Proxy(target, handlers)
  // 存贮处理结果,主要是为了不重复处理
  toProxy.set(target, observed)
  // 同样的是为了不重复处理
  toRaw.set(observed, target)
  // 这里主要是给每一次的代理作个标记,可是这么作有什么好处还没想到 
  if (!targetMap.has(target)) {
    targetMap.set(target, new Map())
  }
  // 返回代理后的对象
  return observed
}

////////////////////下面这些还不太明白///////////////////////

// 判断是否可改
export function isReactive(value: any): boolean {
  return reactiveToRaw.has(value) || readonlyToRaw.has(value)
}

// 判断是否只读
export function isReadonly(value: any): boolean {
  return readonlyToRaw.has(value)
}


export function toRaw<T>(observed: T): T {
  return reactiveToRaw.get(observed) || readonlyToRaw.get(observed) || observed
}

export function markReadonly<T>(value: T): T {
  readonlyValues.add(value)
  return value
}

export function markNonReactive<T>(value: T): T {
  nonReactiveValues.add(value)
  return value
}
复制代码

最后

水平有限,有些地方的注释是本身的猜想,有错漏之处但愿你们指出,感谢社区和群里给我解答的大佬!app

相关文章
相关标签/搜索