Vue 3 reactive.ts 源码理解

Vue 3 的响应式系统是其核心特性之一,它允许我们以声明式的方式处理数据变化。本文将深入分析 Vue 3 中 reactive.ts 文件的实现原理,帮助你全面理解 Vue 3 响应式系统的内部工作机制。

目录

  • [Vue 3 Reactivity 系统完全指南:深入理解 reactive.ts](#Vue 3 Reactivity 系统完全指南:深入理解 reactive.ts "#vue-3-reactivity-%E7%B3%BB%E7%BB%9F%E5%AE%8C%E5%85%A8%E6%8C%87%E5%8D%97%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3-reactivets")
    • 目录
    • [1. 概述](#1. 概述 "#1-%E6%A6%82%E8%BF%B0")
    • [2. 核心 API 详解](#2. 核心 API 详解 "#2-%E6%A0%B8%E5%BF%83-api-%E8%AF%A6%E8%A7%A3")
      • [2.1 reactive](#2.1 reactive "#21-reactive")
      • [2.2 shallowReactive](#2.2 shallowReactive "#22-shallowreactive")
      • [2.3 readonly](#2.3 readonly "#23-readonly")
      • [2.4 shallowReadonly](#2.4 shallowReadonly "#24-shallowreadonly")
    • [3. 工具函数解析](#3. 工具函数解析 "#3-%E5%B7%A5%E5%85%B7%E5%87%BD%E6%95%B0%E8%A7%A3%E6%9E%90")
      • [3.1 isReactive](#3.1 isReactive "#31-isreactive")
      • [3.2 isReadonly](#3.2 isReadonly "#32-isreadonly")
      • [3.3 isShallow](#3.3 isShallow "#33-isshallow")
      • [3.4 isProxy](#3.4 isProxy "#34-isproxy")
      • [3.5 toRaw](#3.5 toRaw "#35-toraw")
      • [3.6 markRaw](#3.6 markRaw "#36-markraw")
      • [3.7 toReactive 和 toReadonly](#3.7 toReactive 和 toReadonly "#37-toreactive-%E5%92%8C-toreadonly")
    • [4. 内部实现机制](#4. 内部实现机制 "#4-%E5%86%85%E9%83%A8%E5%AE%9E%E7%8E%B0%E6%9C%BA%E5%88%B6")
      • [4.1 createReactiveObject](#4.1 createReactiveObject "#41-createreactiveobject")
      • [4.2 缓存机制](#4.2 缓存机制 "#42-%E7%BC%93%E5%AD%98%E6%9C%BA%E5%88%B6")
      • [4.3 目标类型检测](#4.3 目标类型检测 "#43-%E7%9B%AE%E6%A0%87%E7%B1%BB%E5%9E%8B%E6%A3%80%E6%B5%8B")
    • [5. TypeScript 高级特性应用](#5. TypeScript 高级特性应用 "#5-typescript-%E9%AB%98%E7%BA%A7%E7%89%B9%E6%80%A7%E5%BA%94%E7%94%A8")
      • [5.1 泛型与条件类型](#5.1 泛型与条件类型 "#51-%E6%B3%9B%E5%9E%8B%E4%B8%8E%E6%9D%A1%E4%BB%B6%E7%B1%BB%E5%9E%8B")
      • [5.2 映射类型](#5.2 映射类型 "#52-%E6%98%A0%E5%B0%84%E7%B1%BB%E5%9E%8B")
      • [5.3 infer 关键字](#5.3 infer 关键字 "#53-infer-%E5%85%B3%E9%94%AE%E5%AD%97")
      • [5.4 联合类型与交叉类型](#5.4 联合类型与交叉类型 "#54-%E8%81%94%E5%90%88%E7%B1%BB%E5%9E%8B%E4%B8%8E%E4%BA%A4%E5%8F%89%E7%B1%BB%E5%9E%8B")
      • [5.5 unique symbol](#5.5 unique symbol "#55-unique-symbol")
      • [5.6 函数重载](#5.6 函数重载 "#56-%E5%87%BD%E6%95%B0%E9%87%8D%E8%BD%BD")
    • [6. 实际应用示例](#6. 实际应用示例 "#6-%E5%AE%9E%E9%99%85%E5%BA%94%E7%94%A8%E7%A4%BA%E4%BE%8B")
    • [7. 性能优化策略](#7. 性能优化策略 "#7-%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96%E7%AD%96%E7%95%A5")
    • [8. 总结](#8. 总结 "#8-%E6%80%BB%E7%BB%93")

1. 概述

reactive.ts 是 Vue 3 响应式系统的核心模块之一,负责创建和管理响应式对象。它通过 JavaScript 的 Proxy API 实现数据拦截,并结合 WeakMap 缓存机制来优化性能。

该模块提供了多种创建响应式数据的方法,满足不同场景的需求:

  • reactive: 创建深层响应式对象
  • shallowReactive: 创建浅层响应式对象
  • readonly: 创建深层只读对象
  • shallowReadonly: 创建浅层只读对象

同时提供了一系列工具函数用于检测和操作响应式对象。

2. 核心 API 详解

2.1 reactive

reactive 是最常用的响应式 API,它创建一个深层响应式的代理对象。

typescript 复制代码
export function reactive<T extends object>(target: T): Reactive<T>

特点:

  • 深度转换:对象的所有嵌套属性都会被转换为响应式
  • 自动解包 ref:如果属性是 ref,则会自动解包但仍保持响应性
  • 不会代理非可扩展对象(如冻结对象)

使用示例:

javascript 复制代码
import { reactive } from 'vue'

const state = reactive({
  count: 0,
  nested: {
    foo: 'bar'
  }
})

// 访问响应式属性
console.log(state.count) // 0

// 修改响应式属性
state.count++
state.nested.foo = 'baz'

2.2 shallowReactive

shallowReactive 创建一个浅层响应式对象,只有根级别的属性是响应式的。

typescript 复制代码
export function shallowReactive<T extends object>(target: T): ShallowReactive<T>

特点:

  • 只有根级别属性是响应式的
  • 不会自动解包 ref 值
  • 不会对嵌套对象进行递归响应式处理

使用示例:

javascript 复制代码
import { shallowReactive } from 'vue'

const state = shallowReactive({
  foo: 1,
  nested: {
    bar: 2
  }
})

// 修改根级属性是响应式的
state.foo++
console.log(state.foo) // 2

// 嵌套对象不是响应式的
state.nested.bar++
// 不会触发响应式更新

2.3 readonly

readonly 创建一个只读代理对象,所有修改操作都会被拦截并警告。

typescript 复制代码
export function readonly<T extends object>(target: T): DeepReadonly<UnwrapNestedRefs<T>>

特点:

  • 深度只读:所有嵌套属性都是只读的
  • 保持响应性:可以追踪变化并触发依赖更新
  • 适用于需要防止意外修改的场景

使用示例:

javascript 复制代码
import { reactive, readonly } from 'vue'

const original = reactive({ count: 0 })
const copy = readonly(original)

// 读取操作正常工作
console.log(copy.count) // 0

// 修改原始对象会触发依赖更新
original.count++

// 修改副本会发出警告
copy.count++ // warning!

2.4 shallowReadonly

shallowReadonly 创建一个浅层只读对象,只有根级别的属性是只读的。

typescript 复制代码
export function shallowReadonly<T extends object>(target: T): Readonly<T>

特点:

  • 只有根级别属性是只读的
  • 不会自动解包 ref 值
  • 嵌套对象可以被修改

使用示例:

javascript 复制代码
import { shallowReadonly } from 'vue'

const state = shallowReadonly({
  foo: 1,
  nested: {
    bar: 2
  }
})

// 修改根级属性会被拦截
state.foo++ // 警告

// 嵌套对象可以被修改
state.nested.bar++ // 正常工作

3. 工具函数解析

3.1 isReactive

检查对象是否是由 reactiveshallowReactive 创建的代理。

typescript 复制代码
export function isReactive(value: unknown): boolean

使用示例:

javascript 复制代码
import { reactive, readonly, ref, shallowReactive } from 'vue'

console.log(isReactive(reactive({})))            // true
console.log(isReactive(readonly(reactive({}))) ) // true
console.log(isReactive(ref({}).value))           // true
console.log(isReactive(ref(true)))               // false
console.log(isReactive(shallowReactive({})))     // true

3.2 isReadonly

检查对象是否是只读的。

typescript 复制代码
export function isReadonly(value: unknown): boolean

使用示例:

javascript 复制代码
import { reactive, readonly, shallowReadonly } from 'vue'

console.log(isReadonly(readonly({})))         // true
console.log(isReadonly(shallowReadonly({})))  // true
console.log(isReadonly(reactive({})))         // false

3.3 isShallow

检查对象是否是浅层的。

typescript 复制代码
export function isShallow(value: unknown): boolean

3.4 isProxy

检查对象是否是由 reactive、readonly、shallowReactive 或 shallowReadonly 创建的代理。

typescript 复制代码
export function isProxy(value: any): boolean

3.5 toRaw

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

typescript 复制代码
export function toRaw<T>(observed: T): T

使用示例:

javascript 复制代码
import { reactive, toRaw } from 'vue'

const foo = {}
const reactiveFoo = reactive(foo)

console.log(toRaw(reactiveFoo) === foo) // true

3.6 markRaw

标记一个对象,使其永远不会被转换为代理。

typescript 复制代码
export function markRaw<T extends object>(value: T): Raw<T>

使用示例:

javascript 复制代码
import { reactive, markRaw, isReactive } from 'vue'

const foo = markRaw({})
console.log(isReactive(reactive(foo))) // false

// 也适用于嵌套对象
const bar = reactive({ foo })
console.log(isReactive(bar.foo)) // false

3.7 toReactive 和 toReadonly

这两个辅助函数用于有条件地创建响应式或只读代理。

typescript 复制代码
export const toReactive = <T extends unknown>(value: T): T =>
  isObject(value) ? reactive(value) : value

export const toReadonly = <T extends unknown>(value: T): DeepReadonly<T> =>
  isObject(value) ? readonly(value) : (value as DeepReadonly<T>)

4. 内部实现机制

4.1 createReactiveObject

所有公共 API 都通过 createReactiveObject 函数实现:

typescript 复制代码
function createReactiveObject(
  target: Target,
  isReadonly: boolean,
  baseHandlers: ProxyHandler<any>,
  collectionHandlers: ProxyHandler<any>,
  proxyMap: WeakMap<Target, any>,
)

该函数处理了多种边界情况:

  1. 检查目标是否为对象
  2. 避免重复代理
  3. 处理只读和响应式的特殊情况
  4. 根据对象类型选择合适的处理器

4.2 缓存机制

通过 WeakMap 缓存已创建的代理对象,避免重复创建:

typescript 复制代码
export const reactiveMap: WeakMap<Target, any> = new WeakMap<Target, any>()
export const shallowReactiveMap: WeakMap<Target, any> = new WeakMap<Target, any>()
export const readonlyMap: WeakMap<Target, any> = new WeakMap<Target, any>()
export const shallowReadonlyMap: WeakMap<Target, any> = new WeakMap<Target, any>()

4.3 目标类型检测

通过 TargetType 枚举和相关函数区分不同类型的对象:

typescript 复制代码
enum TargetType {
  INVALID = 0,
  COMMON = 1,
  COLLECTION = 2,
}

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
  }
}

5. TypeScript 高级特性应用

5.1 泛型与条件类型

typescript 复制代码
export type UnwrapNestedRefs<T> = T extends Ref ? T : UnwrapRefSimple<T>

这个条件类型用于判断是否需要解包嵌套的 refs。

5.2 映射类型

typescript 复制代码
export type DeepReadonly<T> = T extends Builtin
  ? T
  : T extends Map<infer K, infer V>
    ? ReadonlyMap<DeepReadonly<K>, DeepReadonly<V>>
    // ... 更多条件分支
    : T extends {}
      ? { readonly [K in keyof T]: DeepReadonly<T[K]> }
      : Readonly<T>

使用映射类型递归创建深度只读类型。

5.3 infer 关键字

DeepReadonly<T> 类型中使用 infer 提取泛型参数:

typescript 复制代码
T extends Map<infer K, infer V>
  ? ReadonlyMap<DeepReadonly<K>, DeepReadonly<V>>

5.4 联合类型与交叉类型

typescript 复制代码
type Primitive = string | number | boolean | bigint | symbol | undefined | null

export type Reactive<T> = UnwrapNestedRefs<T> &
  (T extends readonly any[] ? ReactiveMarker : {})

5.5 unique symbol

使用 unique symbol 创建全局唯一的标识符:

typescript 复制代码
declare const ReactiveMarkerSymbol: unique symbol

export interface ReactiveMarker {
  [ReactiveMarkerSymbol]?: void
}

5.6 函数重载

typescript 复制代码
export function reactive<T extends object>(target: T): Reactive<T>
export function reactive(target: object)

通过函数重载提供更精确的类型定义。

6. 实际应用示例

javascript 复制代码
import { reactive, readonly, isReactive, toRaw } from 'vue'

// 创建响应式状态
const state = reactive({
  users: [],
  loading: false,
  error: null
})

// 创建只读状态供组件使用
const stateReader = readonly(state)

// 在组件中使用
export default {
  data() {
    return {
      state: stateReader
    }
  },
  methods: {
    async loadUsers() {
      // 修改原始状态
      state.loading = true
      try {
        const users = await fetchUsers()
        state.users = users
      } catch (error) {
        state.error = error.message
      } finally {
        state.loading = false
      }
    }
  },
  computed: {
    userCount() {
      // 通过只读代理访问
      return this.state.users.length
    }
  }
}

7. 性能优化策略

  1. 缓存机制:通过 WeakMap 避免重复创建代理对象
  2. 类型区分:为不同类型的对象(普通对象、集合)使用不同的处理器
  3. 跳过标记 :通过 markRaw 跳过不需要响应式的对象
  4. 浅层处理 :在不需要深度响应式时使用 shallowReactive

8. 总结

Vue 3 的 reactive.ts 文件通过巧妙地结合 Proxy API、WeakMap 缓存和 TypeScript 高级类型系统,实现了强大而灵活的响应式系统。它不仅提供了多种 API 满足不同场景需求,还通过各种优化策略保证了良好的性能。

理解这些实现细节有助于我们:

  1. 更好地使用 Vue 3 的响应式 API
  2. 在遇到问题时能够快速定位和解决
  3. 学习如何在项目中应用类似的模式

通过深入分析这个文件,我们可以看到 Vue 3 在设计上的精妙之处,以及如何通过 TypeScript 的类型系统提供既安全又灵活的 API。

相关推荐
灰海1 小时前
vue中通过heatmap.js实现热力图(多个热力点)热区展示(带鼠标移入弹窗)
前端·javascript·vue.js·heatmap·heatmapjs
码上暴富3 小时前
vue2迁移到vite[保姆级教程]
前端·javascript·vue.js
老华带你飞3 小时前
考研论坛平台|考研论坛小程序系统|基于java和微信小程序的考研论坛平台小程序设计与实现(源码+数据库+文档)
java·vue.js·spring boot·考研·小程序·毕设·考研论坛平台小程序
YAY_tyy4 小时前
基于 Vue3 + VueOffice 的多格式文档预览组件实现(支持 PDF/Word/Excel/PPT)
前端·javascript·vue.js·pdf·word·excel
m0_748461396 小时前
Spring Boot + Vue 项目中使用 Redis 分布式锁案例
vue.js·spring boot·redis
珍珠奶茶爱好者7 小时前
vue二次封装ant-design-vue的table,识别columns中的自定义插槽
前端·javascript·vue.js
Slice_cy7 小时前
深入剖析 Vue 响应式系统:从零实现一个精简版
vue.js
羊羊小栈8 小时前
基于「YOLO目标检测 + 多模态AI分析」的PCB缺陷检测分析系统(vue+flask+数据集+模型训练)
vue.js·人工智能·yolo·目标检测·flask·毕业设计·大作业
晚星star8 小时前
在 Web 前端实现流式 TTS 播放
前端·vue.js
本末倒置1838 小时前
前端面试高频题:18个经典技术难点深度解析与解决方案
前端·vue.js·面试