跳转到内容

最基本的 reactive 实现

实现一个最简单的 reactive 函数,能够:

  • 使用 Proxy 拦截对象的读取和修改操作
  • 在读取属性时调用 track 收集依赖
  • 在修改属性时调用 trigger 触发更新

reactive 函数接收一个普通对象,返回一个 Proxy 代理对象:

vue-source/packages/reactivity/src/reactive.ts
import { track, trigger } from './effect'
export function reactive<T extends object>(target: T): T
export function reactive(target: object) {
const proxy = new Proxy(target, {
get(target, key) {
// 收集依赖
track(target, key)
// 返回对象的相应属性值
const result = Reflect.get(target, key)
return result
},
set(target, key, value) {
// 触发更新
trigger(target, key)
// 设置对象的相应属性值
const result = Reflect.set(target, key, value)
return result
},
})
return proxy
}

目前只是打印日志,后续会实现真正的依赖收集和触发更新:

vue-source/packages/reactivity/src/effect.ts
export function track(target: object, key: unknown) {
console.log(`target ${JSON.stringify(target)} 依赖收集:${key}属性被读取了`)
}
export function trigger(target: object, key: unknown) {
console.log(`target ${JSON.stringify(target)} 触发更新:${key}属性被修改了`)
}

在 Proxy 的 handler 中,推荐使用 Reflect 而不是直接操作 target:

// 不推荐
get(target, key) {
return target[key]
}
// 推荐
get(target, key) {
return Reflect.get(target, key)
}

原因:

  1. Reflect 方法的返回值更合理(如 Reflect.set 返回布尔值表示是否成功)
  2. Reflect 可以正确处理 this 指向问题
  3. 与 Proxy 的 trap 方法一一对应,语义更清晰

可以阅读 Reflect 原理笔记 结合理解。

export function reactive<T extends object>(target: T): T
export function reactive(target: object) {
// ...
}

使用函数重载可以保留原对象的类型信息,让 TypeScript 能够正确推断返回值类型。

创建一个简单的 HTML 文件测试:

basic-reactive.html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>basic-reactive</title>
</head>
<body>
<script type="module">
import { reactive } from '../dist/reactivity.esm.js'
const raw = { count: 1 }
const proxyRaw = reactive(raw)
// 读取属性,触发 track
console.log(proxyRaw.count)
// 输出: target {"count":1} 依赖收集:count属性被读取了
// 输出: 1
// 修改属性,触发 trigger
proxyRaw.count++
// 输出: target {"count":1} 依赖收集:count属性被读取了
// 输出: target {"count":2} 触发更新:count属性被修改了
</script>
</body>
</html>

这个最基本的 reactive 实现展示了响应式系统的核心思路:

  • 使用 Proxy 拦截对象的读写操作
  • get 中调用 track 收集依赖
  • set 中调用 trigger 触发更新
  • 使用 Reflect 确保操作的正确性

虽然功能还不完整,但已经搭建好了响应式系统的基本框架。