reactive 详解
reactive() 将一个普通对象转为深度响应式代理对象。与 ref 不同,reactive 返回的对象可以直接通过属性访问,不需要 .value。
reactive()
reactive() 创建深度响应式对象代理——所有嵌套对象也会自动变为响应式。
import { reactive, watchEffect } from 'vitarx'
const state = reactive({
name: '张三',
profile: {
age: 25,
city: '北京'
}
})
watchEffect(() => {
console.log('姓名:', state.name)
console.log('城市:', state.profile.city)
})
// ✅ 修改根级属性——触发更新
state.name = '李四'
// ✅ 修改嵌套属性——也触发更新(深度响应式)
state.profile.city = '上海'属性级信号
Vitarx 的 reactive 采用属性级信号机制——对象的每个属性都有独立的依赖追踪。这意味着只有被访问的属性变化时才会触发更新,未访问的属性变化不会影响。
import { reactive, watchEffect } from 'vitarx'
const state = reactive({ name: '张三', age: 25 })
watchEffect(() => {
// 只访问了 name 属性,只追踪 name 的依赖
console.log('姓名:', state.name)
})
// ✅ 触发更新——name 变了
state.name = '李四'
// ❌ 不会触发更新——age 变了,但副作用中没有访问 age
state.age = 30这种机制使得更新非常精确——只有真正依赖的数据变化时才会重新执行副作用。
shallowReactive()
shallowReactive() 创建浅层响应式对象——只有根级属性是响应式的,嵌套对象不会被代理。
import { shallowReactive, watchEffect } from 'vitarx'
const state = shallowReactive({
name: '张三',
profile: { age: 25, city: '北京' }
})
watchEffect(() => {
console.log('state 变化了')
})
// ✅ 触发更新——修改根级属性
state.name = '李四'
// ❌ 不会触发更新——修改嵌套属性
state.profile.city = '上海'何时使用 shallowReactive
- 对象嵌套层级很深,深层代理有性能问题
- 嵌套数据由第三方库管理,不需要框架代理
- 你只关心根级属性的变化
集合类型支持
reactive() 支持以下集合类型:Map、Set、WeakMap、WeakSet。
Map
import { reactive, watchEffect } from 'vitarx'
const userMap = reactive(new Map<string, number>())
watchEffect(() => {
console.log('Map 大小:', userMap.size)
})
userMap.set('张三', 25) // 触发更新
userMap.delete('张三') // 触发更新
userMap.clear() // 触发更新Set
import { reactive, watchEffect } from 'vitarx'
const tagSet = reactive(new Set<string>())
watchEffect(() => {
console.log('Set 大小:', tagSet.size)
})
tagSet.add('前端') // 触发更新
tagSet.delete('前端') // 触发更新INFO
集合类型(Map、Set、WeakMap、WeakSet)的响应式代理不区分深度/浅层,始终使用相同的代理策略。
ref 自动解包
在 reactive 对象中,如果属性值是 ref,读取时会自动解包(自动取 .value),不需要手动访问 .value:
import { ref, reactive } from 'vitarx'
const count = ref(0)
const state = reactive({ count })
// 读取时自动解包——不需要写 state.count.value
console.log(state.count) // 0(自动取了 count.value)
// 设置时也会自动解包——直接赋值即可
state.count = 10
console.log(count.value) // 10WARNING
自动解包只发生在读取时。如果你在 reactive 对象中新增一个 ref 属性,它也会被自动解包。但如果你直接替换整个 reactive 对象的 ref 属性为另一个 ref,需要手动处理。
解构陷阱
直接解构 reactive 对象会丢失响应性——解构出来的变量只是普通值,不再是响应式的:
import { reactive } from 'vitarx'
const state = reactive({ name: '张三', age: 25 })
// ❌ 解构后失去响应性
const { name, age } = state
// name 和 age 只是普通字符串和数字使用 toRefs 保持响应性
使用 toRefs() 将 reactive 对象的每个属性转为 ref,解构后仍然保持响应性:
import { reactive, toRefs } from 'vitarx'
const state = reactive({ name: '张三', age: 25 })
// ✅ 使用 toRefs 解构,保持响应性
const { name, age } = toRefs(state)
// name 和 age 是 PropertyRef,与原属性双向绑定
name.value = '李四'
console.log(state.name) // '李四'更多 toRefs 的用法请参考 ref 详解。
toRaw()
toRaw() 获取响应式对象的原始对象(未经代理的普通对象):
import { reactive, toRaw } from 'vitarx'
const state = reactive({ name: '张三', age: 25 })
const raw = toRaw(state)
// raw 是原始对象,不是代理对象
console.log(raw === state) // false何时使用 toRaw
- 需要将响应式对象传给第三方库,避免代理带来的兼容性问题
- 需要读取原始值而不触发依赖追踪
- 调试时查看原始数据
markRaw()
markRaw() 标记一个对象,使其永远不会被转为响应式代理:
import { reactive, markRaw, isReactive } from 'vitarx'
// 标记对象为"原始"
const config = markRaw({ theme: 'dark', version: 2 })
// 在 reactive 中使用——不会被代理
const state = reactive({ name: '张三', config })
// config 仍然是普通对象
console.log(isReactive(state.config)) // false何时使用 markRaw
- 第三方库的实例对象(如 Chart 实例、DOM 元素),不需要也不应该被代理
- 包含不可变数据的对象,避免代理带来的性能开销
- 包含函数或方法的对象,代理可能导致
this绑定问题
WARNING
markRaw() 只能标记对象类型,传入非对象会抛出 TypeError。标记是永久的,无法撤销。
isReactive()
isReactive() 判断一个值是否为 reactive() 或 shallowReactive() 创建的响应式对象:
import { reactive, shallowReactive, ref, isReactive } from 'vitarx'
const state = reactive({ count: 0 })
const shallow = shallowReactive({ count: 0 })
const countRef = ref(0)
isReactive(state) // true
isReactive(shallow) // true
isReactive(countRef) // false
isReactive({}) // false完整示例
下面是一个综合使用 reactive 相关 API 的示例:
import {
reactive,
shallowReactive,
toRefs,
toRaw,
markRaw,
isReactive,
ref,
createApp
} from 'vitarx'
function App() {
// 深度响应式
const user = reactive({
name: '张三',
profile: { age: 25, city: '北京' }
})
// 浅层响应式
const settings = shallowReactive({
theme: 'light',
config: { fontSize: 14 }
})
// ref 自动解包
const count = ref(0)
const state = reactive({ count })
// toRefs 解构
const { name, profile } = toRefs(user)
// markRaw 标记不需要代理的对象
const chartInstance = markRaw({ type: 'bar', data: [] })
const increment = () => {
state.count++ // 自动解包,不需要写 count.value
}
const changeCity = () => {
// 深度响应式——修改嵌套属性也会触发更新
profile.value.city = '上海'
}
const toggleTheme = () => {
// 浅层响应式——修改根级属性触发更新
settings.theme = settings.theme === 'light' ? 'dark' : 'light'
}
const logRaw = () => {
// 获取原始对象
console.log('原始对象:', toRaw(user))
}
return (
<div style={{ padding: '20px' }}>
<h2>reactive 详解示例</h2>
<div style={{ marginTop: '16px' }}>
<h3>深度响应式</h3>
<p>姓名: {name}</p>
<p>年龄: {profile.value.age}岁</p>
<p>城市: {profile.value.city}</p>
<button onClick={changeCity}>切换城市</button>
</div>
<div style={{ marginTop: '16px' }}>
<h3>ref 自动解包</h3>
<p>计数: {state.count}</p>
<button onClick={increment}>+1</button>
</div>
<div style={{ marginTop: '16px' }}>
<h3>浅层响应式</h3>
<p>主题: {settings.theme}</p>
<button onClick={toggleTheme}>切换主题</button>
</div>
<div style={{ marginTop: '16px' }}>
<button onClick={logRaw}>打印原始对象</button>
<p>markRaw 对象是否为响应式: {String(isReactive(chartInstance))}</p>
</div>
</div>
)
}
createApp(App).mount('#app')下一步
- readonly 详解 — 学习如何创建只读代理保护数据