Vue源码分析--reactivity => reactive.ts

1. reactive

  • createReactiveObject

    • 该方法传入五个参数,分别为:

    • target: Target

      • 意义 :这是要被转换成响应式对象的原始对象。在 Vue 的响应式系统中,任何可以被 Proxy 包装的 JavaScript 对象或集合都可以作为 target
      • 用途:这个参数是响应式转换的直接目标,函数将基于这个目标对象创建一个响应式代理。
    • isReadonly: boolean

      • 意义:这个布尔值指示创建的响应式对象是否应该是只读的。
      • 用途 :如果设置为 true,则代理将拦截所有尝试修改目标对象的操作,并可能抛出警告或错误,阻止这些修改。
    • baseHandlers: ProxyHandler<any>

      • 意义 :这是一个包含各种拦截函数(如 get, set, deleteProperty 等)的对象,用于普通对象的响应式处理。
      • 用途:这些处理器定义了对普通对象进行操作时的行为,例如如何追踪依赖、触发更新等。
    • collectionHandlers: ProxyHandler<any>

      • 意义 :这是一个专为处理 JavaScript 的集合类型(如 Map, Set, WeakMap, WeakSet 等)设计的拦截处理器集。
      • 用途:集合类型由于其内部结构和使用方式的特殊性,需要不同的拦截逻辑来实现响应式功能
    • proxyMap: WeakMap<Target, any>

      • 意义 :这是一个 WeakMap,用于存储原始目标对象与其代理对象之间的映射。
      • 用途 :通过这个映射,Vue 能够为同一个目标对象重复使用同一个代理,避免创建多个代理导致的内存泄漏和性能问题。同时,WeakMap 的使用确保了当原始对象不再被引用时,其代理对象也可以被垃圾回收,从而优化了内存使用。
    • 进入该函数:

      typescript 复制代码
       //判断传入的东西是否为对象,只有对象类型才可以,如果不是对象直接返回,如果是在开发环境中返回警告
       if (!isObject(target)) {
           if (__DEV__) {
             warn(`value cannot be made reactive: ${String(target)}`)
           }
           return target
         }
       //这里的isObject
       export const isObject = (val: unknown): val is Record<any, any> =>
         val !== null && typeof val === 'object'
    • 继续进行检查

      kotlin 复制代码
       // 如果对象已经是一个Proxy,返回
       // exception: calling readonly() on a reactive object
         if (
           target[ReactiveFlags.RAW] &&
           !(isReadonly && target[ReactiveFlags.IS_REACTIVE])
         ) {
           return target
         }
       //target[ReactiveFlags.RAW]:这个条件检查 target 对象是否具有 ReactiveFlags.RAW 标志。在 Vue 3 中,ReactiveFlags.RAW 标志用于存储原始对象的引用,通常在对象被转换成响应式对象时设置。如果该属性存在,说明 target 已经是一个通过 reactive 或类似方法处理过的响应式代理,其 RAW 属性指向未被代理的原始对象。
       //isReadonly && target[ReactiveFlags.IS_REACTIVE]:这里检查两个条件:isReadonly 是否为真,以及 target 是否具有 ReactiveFlags.IS_REACTIVE 标志。isReadonly 表明当前的操作是试图创建一个只读响应式对象,而 ReactiveFlags.IS_REACTIVE 检查 target 是否已经是一个响应式对象。
       //该代码本质上还是优化Vue的性能
    • 继续

      kotlin 复制代码
         // target already has corresponding Proxy
       const existingProxy = proxyMap.get(target)
         if (existingProxy) {
           return existingProxy
         }
       //这段代码中的proxyMap指的是Vue中已经创建和缓存的对象
       //在Vue中使用的是weakMap这种对象,这种对象可以使Obj作为key,而且垃圾收集器会自动回收这个指代的对象
       //换句话说,如果没有其他引用指向这个键对象,它会被垃圾回收掉,其对应的值(响应式代理)也会随之消失。
      javascript 复制代码
        // only specific value types can be observed.
       //只有特定类型的值可以被监听
         const targetType = getTargetType(target)
         if (targetType === TargetType.INVALID) {
           return target
         }
       ​
       ​
       //下面是获取目标的类型方法
       function getTargetType(value: Target) {
         return value[ReactiveFlags.SKIP] || !Object.isExtensible(value)
           ? TargetType.INVALID
           : targetTypeMap(toRawType(value))
       }
       //value[ReactiveFlags.SKIP] || !Object.isExtensible(value):value[ReactiveFlags.SKIP]:这部分检查目标对象 value 是否具有 ReactiveFlags.SKIP 标志。在 Vue 3 中,如果一个对象的 __v_skip 属性(对应 ReactiveFlags.SKIP)被设置为 true,则该对象不会被转换为响应式对象。这常用于跳过不需要响应式处理的内部对象或第三方库对象。
       //!Object.isExtensible(value):这个检查使用 Object.isExtensible() 方法来判断目标对象 value 是否是可扩展的(即能否添加新的属性)。如果一个对象不是可扩展的,那么它也不适合转换成响应式对象,因为响应式系统需要能够添加响应式处理所需的内部属性。
       //如果以上一个为真,就直接返回INVALID,不适合转换
       ​
       //使用了Vue中的一个内置的方法,该方法通通过获取转为String的类型,获取准确的方法
       export const toRawType = (value: unknown): string => {
         // extract "RawType" from strings like "[object RawType]"
         return toTypeString(value).slice(8, -1)
       }
       ​
       //再使用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
         }
       }
       //获取对应的类型0,1,2
      csharp 复制代码
       //创建代理对象
       const proxy = new Proxy(
           target,
           targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers,
         )
       //Proxy有两个参数,第一个为代理对象,第二个为当方法变动处理的方法
       //这里的第二个参数表示是否为特殊类型collectionHandlers || baseHandlers
         proxyMap.set(target, proxy)
       //在weakMap缓存中存入该对象key:value
         return proxy
       ​

2.isReactive

Checks if an object is a proxy created by ,检查是否为Proxy创建

scss 复制代码
 export function isReactive(value: unknown): boolean {
   if (isReadonly(value)) {
     return isReactive((value as Target)[ReactiveFlags.RAW])
   }
   return !!(value && (value as Target)[ReactiveFlags.IS_REACTIVE])
 }
 //检查对象,如果是isReadonly,就会去尝试读取源对象eactiveFlags.RAW
 //如果为不是只读,读取ReactiveFlags.IS_REACTIVE 标志
 //!!返回结果

3.isReadonly,isShallow, isProxy

都是类似的操作,通过读取原始对象中的isXXX来确定是否为对应的类型

4. toRaw

返回Vue创建的代理的原始对象

csharp 复制代码
 export function toRaw<T>(observed: T): T {
   const raw = observed && (observed as Target)[ReactiveFlags.RAW]
   return raw ? toRaw(raw) : observed
 }
 ​
 //这里的RAW是Vue中的ReactiveFlags中的一个,表示原始对象
 //递归解包,获取最底层的数据

5.MarkRaw

该方法会标记对象,使其不会转换为代理,返回对象本身。

typescript 复制代码
 export function markRaw<T extends object>(value: T): Raw<T> {
   //检查对象是否可拓展,只有可拓展的函数才可以修改
   if (Object.isExtensible(value)) {
     def(value, ReactiveFlags.SKIP, true)
   }
   return value
 }
 //该方法会标记对象,如果该对象式可继承的,通过def函数,修改函数的对象
 export const def = (obj: object, key: string | symbol, value: any) => {
   Object.defineProperty(obj, key, {
     configurable: true,
     enumerable: false,
     value,
   })
 }
 //给该函数标记跳过
 //主要用于第三方库和内部系统简化处理

6.toReactive

typescript 复制代码
 export const toReactive = <T extends unknown>(value: T): T =>
   //判断是否为对象,如果是直接进行toReactive处理,如果不是直接返回原始数据
   isObject(value) ? reactive(value) : value
 // return createReactiveObject(
 //    target,
 //  false,
 //  mutableHandlers,
 //  mutableCollectionHandlers,
 //  reactiveMap,
 //)

7.toReadonly

返回一个只读的proxy对象,同样调用toReactive处理,但是传入参数不同

typescript 复制代码
 export const toReadonly = <T extends unknown>(value: T): DeepReadonly<T> =>
   isObject(value) ? readonly(value) : (value as DeepReadonly<T>)
 ​
 //return createReactiveObject(
 //  target,
 //  true,
 //  readonlyHandlers,
 //  readonlyCollectionHandlers,
 //  readonlyMap,
 //)
相关推荐
理想不理想v1 分钟前
使用JS实现文件流转换excel?
java·前端·javascript·css·vue.js·spring·面试
惜.己21 分钟前
Jmeter中的配置原件(四)
java·前端·功能测试·jmeter·1024程序员节
EasyNTS22 分钟前
无插件H5播放器EasyPlayer.js网页web无插件播放器vue和react详细介绍
前端·javascript·vue.js
guokanglun1 小时前
Vue.js动态组件使用
前端·javascript·vue.js
Go4doom1 小时前
vue-cli3+qiankun迁移至rsbuild
前端
-seventy-1 小时前
Ajax 与 Vue 框架应用点——随笔谈
前端
我认不到你1 小时前
antd proFromSelect 懒加载+模糊查询
前端·javascript·react.js·typescript
集成显卡1 小时前
axios平替!用浏览器自带的fetch处理AJAX(兼容表单/JSON/文件上传)
前端·ajax·json
焚琴煮鹤的熊熊野火1 小时前
前端垂直居中的多种实现方式及应用分析
前端
我是苏苏2 小时前
C# Main函数中调用异步方法
前端·javascript·c#