Jinuss's blog Jinuss's blog
首页
  • 源码合集

    • Leaflet源码分析
    • Openlayers源码合集
    • vue3源码
  • HTML
  • CSS
  • 技术文档
  • GitHub技巧
  • 学习
  • 实用技巧
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

东流

Web、WebGIS技术博客
首页
  • 源码合集

    • Leaflet源码分析
    • Openlayers源码合集
    • vue3源码
  • HTML
  • CSS
  • 技术文档
  • GitHub技巧
  • 学习
  • 实用技巧
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • reactivity响应式

    • ref
    • reactive
      • 概览
      • 源码分析
        • isReadonly
        • reactive
        • createReactiveObject
        • readonly/shallowReactive/shallowReadonly
        • isReactive
        • isShallow / isProxy / isReadonly
        • markRaw
        • toReadonly
    • 基础对象的代理
    • 数组代理的方法
    • 集合对象的代理
    • Reflect和Proxy详解
    • 依赖的收集与触发
    • effectScope解析
    • effect解析
    • reactive响应式依赖的收集与触发监听
    • 批量更新实现
    • ReactiveEffect类介绍
    • computed
  • 5.18源码学习》
  • reactivity响应式
东流
2025-08-13
目录

reactive

# 概览

vue3中reactive用于将普通对象转换为响应式对象,它的实现原理是通过Proxy和Reflect来实现的。具体的实现文件参见packages\reactivity\src\reactive.ts。本文会介绍reactive的相关api如下:

  • reactive:将普通对象转换为响应式对象
  • readonly:将普通对象转换为只读响应式对象
  • isReactive:判断一个对象是否是响应式对象
  • isReadonly:判断一个对象是否是只读响应式对象
  • isShallow:判断一个对象是否是浅层响应式对象
  • isProxy:判断一个对象是否是代理对象
  • shallowReactive:创建一个浅层响应式对象
  • shallowReadonly:创建一个浅层只读响应式对象
  • markRaw:标记一个对象为原始对象,避免被转换为响应式对象
  • toReadonly:将一个响应式对象转换为只读响应式对象

# 源码分析

在分析reactive.ts的源码之前,先了解如下几个变量,它们分别是:

const reactiveMap = /* @__PURE__ */ new WeakMap(); // 响应式对象的缓存Map
const shallowReactiveMap = /* @__PURE__ */ new WeakMap(); // 浅层响应式对象的缓存Map
const readonlyMap = /* @__PURE__ */ new WeakMap(); // 只读响应式对象的缓存Map
const shallowReadonlyMap = /* @__PURE__ */ new WeakMap(); // 浅层只读响应式对象的缓存Map
1
2
3
4

# isReadonly

isReadonly用于判断一个对象是否是只读响应式对象,它的实现如下:

function isReadonly(value) {
  return !!(value && value["__v_isReadonly"]);
}
1
2
3

isReadonly就是判断参数value的__v_isReadonly属性值的布尔值,若为true则表示是只读对象;否则不是只读对象。

# reactive

reactive的实现如下:

function reactive(target) {
  // 判断target是否是只读对象,若是,则直接返回
  if (isReadonly(target)) {
    return target;
  }
  // 调用 createReactiveObject 函数创建响应式对象,并返回
  return createReactiveObject(
    target, // 目标对象
    false, // 是否只读,默认为false
    mutableHandlers, // 普通对象的代理处理函数
    mutableCollectionHandlers, // 集合对象的代理处理函数
    reactiveMap // 响应式对象的缓存Map
  );
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# createReactiveObject

reactive的核心实现是createReactiveObject函数,它的实现如下:

function createReactiveObject(target, isReadonly2, baseHandlers, collectionHandlers, proxyMap) {
  // 判断target是否是对象,若不是,则弹出警告,并直接返回
  if (!isObject(target)) {
    {
      warn(
        `value cannot be made ${isReadonly2 ? "readonly" : "reactive"}: ${String(
          target
        )}`
      );
    }
    return target;
  }
  if (target["__v_raw"] && !(isReadonly2 && target["__v_isReactive"])) {
    return target;
  }
   const targetType = getTargetType(target);
  if (targetType === 0 /* INVALID */) {
    return target;
  }

  const existingProxy = proxyMap.get(target);
  if (existingProxy) {
    return existingProxy;
  }
  
  const proxy = new Proxy(
    target,
    targetType === 2 /* COLLECTION */ ? collectionHandlers : baseHandlers
  );
  proxyMap.set(target, proxy);
  return proxy;
}

function targetTypeMap(rawType) {
  switch (rawType) {
    case "Object":
    case "Array":
      return 1 /* COMMON */;
    case "Map":
    case "Set":
    case "WeakMap":
    case "WeakSet":
      return 2 /* COLLECTION */;
    default:
      return 0 /* INVALID */;
  }
}
function getTargetType(value) {
  return value["__v_skip"] || !Object.isExtensible(value) ? 0 /* INVALID */ : targetTypeMap(toRawType(value));
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

createReactiveObject函数接受5个参数,依次为:目标对象target、是否只读isReadonly2、基本类型处理函数baseHandlers、集合对象的代理处理函数collectionHandlers、响应式对象的缓存proxyMap。

createReactiveObject在确保target为对象后,会检测target是一个响应式对象,若是则直接返回;然后会从缓存proxyMap中获取target的代理对象,若存在则直接返回该代理对象;否则调用getTargetType函数判断目标对象target的类型,若类型为INVALID,则直接返回target;否则根据类型创建代理对象,并将其缓存到proxyMap中,最后返回该代理对象。

getTargetType的实现也在上面,若target存在__v_skip属性且为true,或者target不是可扩展的对象,则返回INVALID;否则根据target的类型调用targetTypeMap函数返回类型。

由上targetTypeMap函数可知,targetTypeMap函数根据target的类型返回一个数字,分别表示:

  • 0:INVALID,表示target不是一个对象
  • 1:COMMON,表示target是一个普通对象或数组
  • 2:COLLECTION,表示target是一个集合对象

综上,可以理解createReactiveObject函数根据target创建代理对象的逻辑如下:

  • 若target为普通对象或数组,则创建普通对象的代理对象,使用baseHandlers处理函数;
  • 若target为Map/Set/WeakMap/WeakSet,则创建集合对象的代理对象,使用collectionHandlers处理函数;
  • 其余情况,则直接返回target。

关于处理器baseHandlers和collectionHandlers的实现,会在后面的章节中介绍。

# readonly/shallowReactive/shallowReadonly

readonly/shallowReactive/shallowReadonly的实现如下:

function readonly(target) {
  return createReactiveObject(
    target,
    true, // 表示只读
    readonlyHandlers, // 普通对象的只读代理处理函数
    readonlyCollectionHandlers, // 集合对象的只读代理处理函数
    readonlyMap // 只读响应式对象的缓存readonlyMap
  );
}

function shallowReactive(target) {
  return createReactiveObject(
    target,
    false, // 不是只读
    shallowReactiveHandlers, // 普通对象的浅层响应式代理处理函数
    shallowCollectionHandlers, // 集合对象的浅层响应式代理处理函数
    shallowReactiveMap // 浅层响应式对象的缓存shallowReactiveMap
  );
}

function shallowReadonly(target) {
  return createReactiveObject(
    target,
    true, // 表示只读
    shallowReadonlyHandlers, // 普通对象的浅层只读代理处理函数
    shallowReadonlyCollectionHandlers, // 集合对象的浅层只读代理处理函数
    shallowReadonlyMap // 浅层只读响应式对象的缓存shallowReadonlyMap
  );
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

readonly的实现和reactive的实现类似,不同就是调用createReactiveObject函数时,传参不同。

类似的还有shallowReactive、shallowReadonly。

它们的不同如下所示

函数 只读 浅层 缓存 普通对象的代理方法 集合对象的代理方法
reactive 否 否 reactiveMap baseHandlers collectionHandlers
shallowReactive 否 是 shallowReactiveMap shallowReactiveHandlers shallowCollectionHandlers
readonly 是 否 readonlyMap readonlyHandlers readonlyCollectionHandlers
shallowReadonly 是 是 shallowReadonlyMap shallowReadonlyHandlers shallowReadonlyCollectionHandlers

# isReactive

isReactive的实现如下:

function isReactive(value) {
  if (isReadonly(value)) {
    return isReactive(value["__v_raw"]);
  }
  return !!(value && value["__v_isReactive"]);
}
1
2
3
4
5
6

isReactive会先判断value是否为只读响应式数据,若为只读响应式数据,则会递归调用isReactive函数判断value的原始数据是否为响应式数据;否则,会判断value是否为响应式数据,若为响应式数据,则返回true,否则返回false。

# isShallow / isProxy / isReadonly

function isShallow(value) {
  return !!(value && value["__v_isShallow"]);
}
function isProxy(value) {
  return value ? !!value["__v_raw"] : false;
}
function isReadonly(value) {
  return !!(value && value["__v_isReadonly"]);
}
1
2
3
4
5
6
7
8
9

isShallow/isProxy/isReadonly的实现都比较简单,都是判断参数是否存在某个属性,若存在则返回true,否则返回false。它们读取的属性__v_isShallow/__v_raw/__v_isReadonly实际上都是针对代理对象的,而它们的逻辑处理也是在处理器方法中实现的,后续会讲到

# markRaw

markRaw用于标记对象为原始对象,这种对象不会被转换为响应式对象,也不会被代理,其实现如下:

function markRaw(value) {
  if (!hasOwn(value, "__v_skip") && Object.isExtensible(value)) {
    def(value, "__v_skip", true);
  }
  return value;
}
1
2
3
4
5
6

markRaw首先会判断对象是否可扩展,若可扩展,则会在对象上定义一个__v_skip属性,打一个标记,值为true;最后返回该对象。使用reactive(target)创建响应式对象时,若target不可扩展,则在调用createReactiveObject时,其内部调用getTargetType的返回值就是0.

def内部就是调用Object.defineProperty定义对象上属性

# toReadonly

toReadonly用于将响应式对象转换为只读对象,其实现如下:

const toReadonly = (value) => isObject(value) ? readonly(value) : value;
1

toReadonly会判断参数是否为对象,若为是,则会调用readonly函数将参数转换为只读响应式对象,并返回该代理对象;否则,直接返回参数。这和toReactive很类似,只是toReactive会将参数转换为响应式对象,而toReadonly会将参数转换为只读响应式对象。

编辑 (opens new window)
上次更新: 2025/08/15, 09:55:22
ref
基础对象的代理

← ref 基础对象的代理→

最近更新
01
computed
08-26
02
ReactiveEffect类介绍
08-26
03
批量更新实现
08-25
更多文章>
Theme by Vdoing | Copyright © 2024-2025 东流 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式