vue3响应式核心文章汇总:
vue3响应式核心之reactive源码详解
vue3响应式核心之effect源码详解
vue3响应式核心分两篇文章讲解,本篇讲解reactive源码和实现原理,下一篇vue3响应式核心之effect源码详解讲解effect依赖收集与触发。
一、Reactive源码
1、reactive
源码路径:packages/reactivity/src/reactive.ts
export function reactive(target: object) {
// if trying to observe a readonly proxy, return the readonly version.
// 是否是只读响应式对象
if (isReadonly(target)) {
return target
}
return createReactiveObject(
target,
false,
mutableHandlers,
mutableCollectionHandlers,
reactiveMap
)
}
当我们执行reactive({})
的时候,会执行createReactiveObject
这个工厂方法,返回一个响应式对象。
2、接着看工厂方法createReactiveObject
源码路径:packages/reactivity/src/reactive.ts
function createReactiveObject(
target: Target,
isReadonly: boolean,
baseHandlers: ProxyHandler<any>,
collectionHandlers: ProxyHandler<any>,
proxyMap: WeakMap<Target, any>
) {
// 仅对对象类型有效(对象、数组和 Map、Set 这样的集合类型),而对 string、number 和 boolean 这样的 原始类型 无效。
if (!isObject(target)) {
if (__DEV__) {
console.warn(`value cannot be made reactive: ${String(target)}`)
}
return target
}
// target is already a Proxy, return it.
// exception: calling readonly() on a reactive object
if (
target[ReactiveFlags.RAW] &&
!(isReadonly && target[ReactiveFlags.IS_REACTIVE])
) {
return target
}
// target already has corresponding Proxy
const existingProxy = proxyMap.get(target)
if (existingProxy) {
return existingProxy
}
// only specific value types can be observed.
const targetType = getTargetType(target)
if (targetType === TargetType.INVALID) {
return target
}
const proxy = new Proxy(
target,
targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
)
proxyMap.set(target, proxy)
return proxy
}
仅对对象类型有效(对象、数组和 Map、Set 这样的集合类型),而对 string、number 和 boolean 这样的 原始类型 无效。
if (!isObject(target)) {
if (__DEV__) {
console.warn(`value cannot be made reactive: ${String(target)}`)
}
return target
}
如果 target 已经是一个代理对象了,那么直接返回 target
if (
target[ReactiveFlags.RAW] &&
!(isReadonly && target[ReactiveFlags.IS_REACTIVE])
) {
return target
}
如果 target 已经有对应的代理对象了,那么直接返回代理对象
const existingProxy = proxyMap.get(target) // 存储响应式对象
if (existingProxy) {
return existingProxy
}
对于不能被观察的类型,直接返回 target
const targetType = getTargetType(target)
if (targetType === TargetType.INVALID) {
return target
}
// getTargetType源码
function getTargetType(value: Target) {
return value[ReactiveFlags.SKIP] || !Object.isExtensible(value) // 不可扩展
? TargetType.INVALID
: targetTypeMap(toRawType(value))
}
// ReactiveFlags枚举
export const enum ReactiveFlags {
// 用于标识一个对象是否不可被转为代理对象,对应的值是 __v_skip
SKIP = '__v_skip',
// 用于标识一个对象是否是响应式的代理,对应的值是 __v_isReactive
IS_REACTIVE = '__v_isReactive',
// 用于标识一个对象是否是只读的代理,对应的值是 __v_isReadonly
IS_READONLY = '__v_isReadonly',
// 用于标识一个对象是否是浅层代理,对应的值是 __v_isShallow
IS_SHALLOW = '__v_isShallow',
// 用于保存原始对象的 key,对应的值是 __v_raw
RAW = '__v_raw'
}
// targetTypeMap
function targetTypeMap(rawType: string) {
switch (rawType) {
case 'Object':
case 'Array':
return TargetType.COMMON
case 'Map':
case 'Set':
case 'WeakMap':
case 'WeakSet':
return TargetType.COLLECTION
default:
return TargetType.INVALID
}
}
// toRawType
export const toRawType = (value: unknown): string => {
// extract "RawType" from strings like "[object RawType]"
return toTypeString(value).slice(8, -1)
}
创建响应式对象(核心代码)
const proxy = new Proxy(
target,
targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
)
接下来将重点讲解baseHandlers
这个回调函数。
二、baseHandlers
1、baseHandlers
baseHandlers
是mutableHandlers
, 来自于 baseHandlers
文件。
mutableHandlers的源码如下,分别对get、set、deleteProperty、has、ownKeys做了代理。
export const mutableHandlers: ProxyHandler<object> = {
get,
set,
deleteProperty,
has,
ownKeys
}
接下来看看这些个拦截器的具体实现。
(1)、get的代理
const get = createGetter()
function createGetter(isReadonly = false, shallow = false) {
// 闭包返回 get 拦截器方法
return function get(target: Target, key: string | symbol, receiver: object) {
// 如果访问的是 __v_isReactive 属性,那么返回 isReadonly 的取反值
if (key === ReactiveFlags.IS_REACTIVE) {
return !isReadonly
// 如果访问的是 __v_isReadonly 属性,那么返回 isReadonly 的值
} else if (key === ReactiveFlags.IS_READONLY) {
return isReadonly
// 如果访问的是 __v_isShallow 属性,那么返回 shallow 的值
} else if (key === ReactiveFlags.IS_SHALLOW) {
return shallow
// 如果访问的是 __v_raw 属性,那么返回 target
} else if (
key === ReactiveFlags.RAW &&
receiver ===
(isReadonly
? shallow
? shallowReadonlyMap
: readonlyMap
: shallow
? shallowReactiveMap
: reactiveMap
).get(target)
) {
return target
}
// target是否是数组
const targetIsArray = isArray(target)
if (!isReadonly) { // 可读
// 如果是数组,并且访问的是数组的一些方法,那么返回对应的方法
if (targetIsArray && hasOwn(arrayInstrumentations, key)) {
// 返回重写的push、pop、 shift、 unshift、 splice 'includes', 'indexOf', 'lastIndexOf'
return Reflect.get(arrayInstrumentations, key, receiver)
}
// 如果访问的是 hasOwnProperty 方法,那么返回 hasOwnProperty 方法
if (key === 'hasOwnProperty') {
return hasOwnProperty
}
}
// 获取 target 的 key 属性值
const res = Reflect.get(target, key, receiver)
// 如果是内置的 Symbol,或者是不可追踪的 key,那么直接返回 res
if (isSymbol(key) ? builtInSymbols.has(key) : isNonTrackableKeys(key)) {
return res
}
// 如果不是只读的,那么进行依赖收集
if (!isReadonly) {
track(target, TrackOpTypes.GET, key)
}
// 如果是浅的,那么直接返回 res
if (shallow) {
return res
}
// 如果 res 是 ref,对返回的值进行解包
if (isRef(res)) {
// ref unwrapping - skip unwrap for Array + integer key.
return targetIsArray && isIntegerKey(key) ? res : res.value
}
// 如果 res 是对象,递归代理
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
}
}
当target是数组的时候,'push', 'pop', 'shift', 'unshift', 'splice'这些方法会改变数组长度,会导致无限递归,因此要先暂停收集依赖, 所以对数组的以上方法进行了拦截和重写
if (targetIsArray && hasOwn(arrayInstrumentations, key)) {
// 返回重写的push、pop、 shift、 unshift、 splice 'includes', 'indexOf', 'lastIndexOf'
return Reflect.get(arrayInstrumentations, key, receiver)
}
重写的代码:
const arrayInstrumentations = createArrayInstrumentations()
function createArrayInstrumentations() {
const instrumentations: Record<string, Function> = {}
// 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[]) {
// 由于上面的方法会改变数组长度,因此暂停收集依赖,不然会导致无限递归
console.log('----自定义push等入口:this, args, key');
pauseTracking()
console.log('----自定义push等暂停收集依赖&执行开始')
// 调用原始方法
const res = (toRaw(this) as any)[key].apply(this, args)
console.log('----自定义push等暂停收集依赖&执行结束')
//复原依赖收集
resetTracking()
return res
}
})
return instrumentations
}
下图是执行结果:
可以用以下代码来理解:
let arr = [1,2,3]
let obj = {
'push': function(...args) {
// 暂停收集依赖逻辑
return Array.prototype.push.apply(this, [...args])
// 启动收集依赖逻辑
}
}
let proxy = new Proxy(arr, {
get: function (target, key, receiver) {
console.log('get的key为 ===>' + key);
let res = '';
if(key === 'push') { //重写push
res = Reflect.get(obj, key, receiver)
} else {
res = Reflect.get(target, key, receiver)
}
return res
},
set(target, key, value, receiver){
console.log('set的key为 ===>' + key, value);
return Reflect.set(target, key, value, receiver);
}
})
proxy.push('99')
特殊属性的不进行依赖收集
// 如果是内置的 Symbol,或者是不可追踪的 key,那么直接返回 res
if (isSymbol(key) ? builtInSymbols.has(key) : isNonTrackableKeys(key)) {
return res;
}
这一步是为了过滤一些特殊的属性,例如原生的Symbol类型的属性,如:Symbol.iterator、Symbol.toStringTag等等,这些属性不需要进行依赖收集,因为它们是内置的,不会改变;
还有一些不可追踪的属性,如:proto、__v_isRef、__isVue这些属性也不需要进行依赖收集;
依赖收集
// 如果不是只读的,那么进行依赖收集
if (!isReadonly) {
track(target, "get" , key);
}
浅的不进行递归代理
if (shallow) {
return res;
}
对返回值进行解包
// 如果 res 是 ref,对返回的值进行解包
if (isRef(res)) {
// 对于数组和整数类型的 key,不进行解包
return targetIsArray && isIntegerKey(key) ? res : res.value;
}
这一步是为了处理ref的情况,如果res是ref,那么就对res进行解包,这里有一个判断,如果是数组,并且key是整数类型,那么就不进行解包;因为reactive是深层响应式的,所以要把属性为ref的进行解包
对象的递归代理
// 如果 res 是对象,那么对返回的值进行递归代理
if (isObject(res)) {
return isReadonly ? readonly(res) : reactive(res);
}
(2)、set的代理
const set = createSetter()
function createSetter(shallow = false) {
// 返回一个set方法
return function set(
target: object,
key: string | symbol,
value: unknown,
receiver: object
): boolean {
let oldValue = (target as any)[key] // 获取旧值
// 如果旧值是只读的,并且是 ref,并且新值不是 ref
if (isReadonly(oldValue) && isRef(oldValue) && !isRef(value)) {
return false
}
if (!shallow) { // 非shallow
// 新值非shallow && 非只读
if (!isShallow(value) && !isReadonly(value)) {
// 获取新旧值的原始值
oldValue = toRaw(oldValue)
value = toRaw(value)
}
// 代理对象非数组 & 旧值是ref & 新值非ref
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
}
console.log('----set', target, key, value)
// 是数组 & key是整型数字 ?
// 如果 key 小于数组的长度,那么就是有这个 key :
// 如果不是数组,那么就是普通对象,直接判断是否有这个 key
// 数组会触发两次set: index和新增的值 和 'length'和新增之后的数组长度
const hadKey =
isArray(target) && isIntegerKey(key)
? Number(key) < target.length
: hasOwn(target, key)
// 设置key-value
const result = Reflect.set(target, key, value, receiver)
// don't trigger if target is something up in the prototype chain of original
// 如果目标对象是原始数据的原型链中的某个元素,则不会触发依赖收集
if (target === toRaw(receiver)) {
if (!hadKey) {// 如果没有这个 key,那么就是新增了一个属性,触发 add 事件
trigger(target, TriggerOpTypes.ADD, key, value)
} else if (hasChanged(value, oldValue)) { // // 如果有这个 key,那么就是修改了一个属性,触发 set 事件
trigger(target, TriggerOpTypes.SET, key, value, oldValue)
}
}
// 返回结果,这个结果为 boolean 类型,代表是否设置成功
return result
}
}
主要逻辑:
获取旧值
let oldValue = target[key];
判断旧值是否是只读的
// 如果旧值是只读的,并且是 ref,并且新值不是 ref,那么直接返回 false,代表设置失败
if (isReadonly(oldValue) && isRef(oldValue) && !isRef(value)) {
return false;
}
以上就是源码分析Vue3响应式核心之reactive的详细内容,更多关于Vue3 reactive的资料请关注编程网其它相关文章!