代码路径:packages/reactivity/src/baseHandlers.ts
附详细注释
import {
reactive,
readonly,
toRaw,
ReactiveFlags,
Target,
readonlyMap,
reactiveMap,
shallowReactiveMap,
shallowReadonlyMap,
isReadonly,
isShallow
} from './reactive'
import { TrackOpTypes, TriggerOpTypes } from './operations'
import {
track,
trigger,
ITERATE_KEY,
pauseTracking,
resetTracking
} from './effect'
import {
isObject,
hasOwn,
isSymbol,
hasChanged,
isArray,
isIntegerKey,
extend,
makeMap
} from '@vue/shared'
import { isRef } from './ref'
import { warn } from './warning'
// 判断一个属性名是否为不可追踪的属性名
// Vue 3 的响应式系统中,只有被追踪的属性才能触发依赖更新,不可追踪的属性则不会触发依赖更新
const isNonTrackableKeys = /*#__PURE__*/ makeMap(`__proto__,__v_isRef,__isVue`)
const builtInSymbols = new Set(
/*#__PURE__*/
// Object.getOwnPropertyNames 方法只能获取到对象自身的属性名,而不能获取到原型链上的属性名
Object.getOwnPropertyNames(Symbol)
// ios10.x Object.getOwnPropertyNames(Symbol) can enumerate 'arguments' and 'caller'
// but accessing them on Symbol leads to TypeError because Symbol is a strict mode
// function
// 使用 filter 方法过滤掉 arguments 和 caller 这两个属性名
// ## 某些旧版本的浏览器中,Object.getOwnPropertyNames(Symbol) 可能会返回 caller 和 arguments,新版本浏览器一般不会
// 因为在某些环境下,这两个属性名可能会导致 TypeError 错误,Symbol 是严格模式
// 这个过滤操作在现代浏览器中并不会有任何影响,但是为了保证代码的兼容性,Vue 3 的源码中还是加入了这个过滤操作
.filter(key => key !== 'arguments' && key !== 'caller')
// 将 Symbol 对象的属性名转换为对应的 Symbol 对象
.map(key => (Symbol as any)[key])
// 过滤掉非 Symbol 对象
.filter(isSymbol)
)
const get = /*#__PURE__*/ createGetter()
const shallowGet = /*#__PURE__*/ createGetter(false, true)
const readonlyGet = /*#__PURE__*/ createGetter(true)
const shallowReadonlyGet = /*#__PURE__*/ createGetter(true, true)
const arrayInstrumentations = /*#__PURE__*/ createArrayInstrumentations()
// 创建 Vue 3 中的数组代理对象的方法。这些方法包括一些数组的常用方法
// 如 push、pop、shift、unshift、splice、includes、indexOf 和 lastIndexOf
// 用于创建数组代理对象的的工厂函数,用于拦截数组的访问和修改,并在访问和修改时进行依赖收集
function createArrayInstrumentations() {
// 存储数组方法
const instrumentations: Record<string, Function> = {}
// instrument identity-sensitive Array methods to account for possible reactive
// values
// 对这三个方法定义代理函数,用于拦截数组的访问,并在访问时进行依赖收集
;(['includes', 'indexOf', 'lastIndexOf'] as const).forEach(key => {
instrumentations[key] = function (this: unknown[], ...args: unknown[]) {
const arr = toRaw(this) as any
for (let i = 0, l = this.length; i < l; i++) {
track(arr, TrackOpTypes.GET, i + '')
}
// we run the method using the original args first (which may be reactive)
const res = arr[key](...args)
if (res === -1 || res === false) {
// if that didn't work, run it again using raw values.
return arr[key](...args.map(toRaw))
} else {
return res
}
}
})
// instrument length-altering mutation methods to avoid length being tracked
// which leads to infinite loops in some cases (#2137)
// 对这五个方法定义代理函数,用于拦截数组修改,并在修改时暂停依赖收集,以避免无限循环
;(['push', 'pop', 'shift', 'unshift', 'splice'] as const).forEach(key => {
instrumentations[key] = function (this: unknown[], ...args: unknown[]) {
pauseTracking()
const res = (toRaw(this) as any)[key].apply(this, args)
resetTracking()
return res
}
})
return instrumentations
}
// 判断一个对象是否具有指定的属性
function hasOwnProperty(this: object, key: string) {
// toRaw:将响应式对象转换为原始对象
const obj = toRaw(this)
// 追踪 obj 对象的 key 属性的访问情况
// track:工具函数,追踪响应式对象的属性访问情况
track(obj, TrackOpTypes.HAS, key)
// 判断 obj 对象是否具有 key 属性
return obj.hasOwnProperty(key)
}
// 创建 Vue 3 中的代理对象的 getter 函数。这个 getter 函数用于拦截对象属性的访问。
// createGetter 函数接受两个参数:isReadonly 和 shallow,分别表示是否创建只读代理和是否创建浅层代理。
function createGetter(isReadonly = false, shallow = false) {
// 返回一个名为 get 的函数,它接受三个参数:target(被代理的对象)、key(访问的属性名)和 receiver(代理对象)
return function get(target: Target, key: string | symbol, receiver: object) {
if (key === ReactiveFlags.IS_REACTIVE) {
// 是否为响应式对象
return !isReadonly
} else if (key === ReactiveFlags.IS_READONLY) {
// 是否为只读对象
return isReadonly
} else if (key === ReactiveFlags.IS_SHALLOW) {
// 是否为浅层代理
return shallow
} else if (
// 访问的属性是 ReactiveFlags.RAW 且 receiver 是当前代理对象
key === ReactiveFlags.RAW &&
receiver ===
(isReadonly
? shallow
? shallowReadonlyMap
: readonlyMap
: shallow
? shallowReactiveMap
: reactiveMap
).get(target)
) {
return target
}
// 判断 target 是否为数组
const targetIsArray = isArray(target)
if (!isReadonly) {
// 不是只读代理,且 target 是数组并访问了特定的数组方法
if (targetIsArray && hasOwn(arrayInstrumentations, key)) {
// 返回数组方法的代理
return Reflect.get(arrayInstrumentations, key, receiver)
}
// 如果访问的属性是 hasOwnProperty,返回 hasOwnProperty 函数
if (key === 'hasOwnProperty') {
return hasOwnProperty
}
}
const res = Reflect.get(target, key, receiver)
// 如果访问的属性是内置 Symbol 或不可跟踪的属性,直接返回 res
if (isSymbol(key) ? builtInSymbols.has(key) : isNonTrackableKeys(key)) {
return res
}
// 不是只读代理,收集 target 的依赖
if (!isReadonly) {
track(target, TrackOpTypes.GET, key)
}
// 是浅层代理
if (shallow) {
return res
}
// 是 ref 对象,对其进行解包
// 数组和整数,跳过解包
if (isRef(res)) {
// ref unwrapping - skip unwrap for Array + integer key.
return targetIsArray && isIntegerKey(key) ? res : res.value
}
// 是对象,将其转换为代理对象 根据 isReadonly 返回只读代理或响应式代理
if (isObject(res)) {
// Convert returned value into a proxy as well. we do the isObject check
// here to avoid invalid value warning. Also need to lazy access readonly
// and reactive here to avoid circular dependency.
return isReadonly ? readonly(res) : reactive(res)
}
return res
}
}
const set = /*#__PURE__*/ createSetter()
const shallowSet = /*#__PURE__*/ createSetter(true)
// 拦截对象属性的设置。createSetter 函数接受一个参数:shallow,表示是否创建浅层代理
function createSetter(shallow = false) {
// target(被代理的对象)、key(设置的属性名)、value(设置的属性值)、 receiver(代理对象)
return function set(
target: object,
key: string | symbol,
value: unknown,
receiver: object
): boolean {
// 获取 target 对象上的旧值 oldValue
let oldValue = (target as any)[key]
// 只读的 ref 对象且 value 不是 ref 对象
if (isReadonly(oldValue) && isRef(oldValue) && !isRef(value)) {
// 不允许设置
return false
}
// 不是浅层代理
if (!shallow) {
// 解包 ref 对象
// 获取原始对象
if (!isShallow(value) && !isReadonly(value)) {
oldValue = toRaw(oldValue)
value = toRaw(value)
}
// 是浅层代理,对象将按原本设置,无论是否为响应式对象
if (!isArray(target) && isRef(oldValue) && !isRef(value)) {
oldValue.value = value
return true
}
} else {
// in shallow mode, objects are set as-is regardless of reactive or not
}
// 判断 target 对象是否已经具有该属性
const hadKey =
isArray(target) && isIntegerKey(key)
? Number(key) < target.length
: hasOwn(target, key)
const result = Reflect.set(target, key, value, receiver)
// don't trigger if target is something up in the prototype chain of original
// 如果 target 是原始 receiver 对象,根据属性是否存在以及值是否发生变化,触发相应的依赖更新
if (target === toRaw(receiver)) {
if (!hadKey) {
trigger(target, TriggerOpTypes.ADD, key, value)
} else if (hasChanged(value, oldValue)) {
trigger(target, TriggerOpTypes.SET, key, value, oldValue)
}
}
return result
}
}
// 对象的指定属性,并在响应式系统中触发该属性的删除操作
function deleteProperty(target: object, key: string | symbol): boolean {
// 判断 target 对象是否具有 key 属性
// hasOwn:工具函数,判断一个对象是否具有指定的属性
const hadKey = hasOwn(target, key)
const oldValue = (target as any)[key]
// 使用 Reflect.deleteProperty 方法删除 target 对象的 key 属性,并将结果存储到 result
const result = Reflect.deleteProperty(target, key)
if (result && hadKey) {
// 删除成功使用 trigger函数触发target对象的 key 属性的删除操作
// trigger:工具函数,触发响应式对象的属性操作
trigger(target, TriggerOpTypes.DELETE, key, undefined, oldValue)
}
return result
}
// 判断一个对象是否具有指定的属性,并在响应式系统中追踪该属性的访问情况
function has(target: object, key: string | symbol): boolean {
// 判断 target 对象是否具有 key 属性
const result = Reflect.has(target, key)
// 如果 key 不是 Symbol 类型,或者 key 是内置的 Symbol 对象之一(即 builtInSymbols 对象中包含 key)
if (!isSymbol(key) || !builtInSymbols.has(key)) {
// 追踪 target 对象的 key 属性的访问情况
track(target, TrackOpTypes.HAS, key)
}
return result
}
// 获取一个对象的所有属性名,并在响应式系统中追踪该对象的迭代操作
function ownKeys(target: object): (string | symbol)[] {
// 追踪 target 对象的迭代操作,并将属性名作为参数传递给 track 函数
track(target, TrackOpTypes.ITERATE, isArray(target) ? 'length' : ITERATE_KEY)
// 获取 target 对象的所有属性名并返回
return Reflect.ownKeys(target)
}
export const mutableHandlers: ProxyHandler<object> = {
get,
set,
deleteProperty,
has,
ownKeys
}
export const readonlyHandlers: ProxyHandler<object> = {
get: readonlyGet,
set(target, key) {
if (__DEV__) {
warn(
`Set operation on key "${String(key)}" failed: target is readonly.`,
target
)
}
return true
},
deleteProperty(target, key) {
if (__DEV__) {
warn(
`Delete operation on key "${String(key)}" failed: target is readonly.`,
target
)
}
return true
}
}
export const shallowReactiveHandlers = /*#__PURE__*/ extend(
{},
mutableHandlers,
{
get: shallowGet,
set: shallowSet
}
)
// Props handlers are special in the sense that it should not unwrap top-level
// refs (in order to allow refs to be explicitly passed down), but should
// retain the reactivity of the normal readonly object.
export const shallowReadonlyHandlers = /*#__PURE__*/ extend(
{},
readonlyHandlers,
{
get: shallowReadonlyGet
}
)