給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

相關文章
相關標籤/搜索