响应式状态
响应式状态是 Vitarx 的核心——当数据变化时,界面会自动更新。本页介绍如何使用 ref 和 reactive 创建响应式数据。
ref
ref 用于创建一个响应式引用。它适合包装基本类型值(数字、字符串、布尔值等),也可以包装对象。
基本用法
通过 .value 读写值:
tsx
import { ref, createApp } from 'vitarx'
function App() {
const count = ref(0)
return (
<div>
<p>计数: {count}</p>
<button onClick={() => count.value++}>+1</button>
</div>
)
}
createApp(App).mount('#app')INFO
在 JSX 模板中使用 ref 时不需要写 .value,框架会自动解包。例如 {count} 等等同于 {count.value}。但在 JavaScript 代码中(如事件处理函数、计算逻辑中),必须通过 .value 访问。
包装对象
ref 也可以包装对象,内部会自动将对象转为响应式:
tsx
const user = ref({ name: '张三', age: 25 })
// 读取
console.log(user.value.name) // '张三'
// 修改属性
user.value.age = 26
// 替换整个对象
user.value = { name: '李四', age: 30 }reactive
reactive 用于创建响应式对象。与 ref 不同,reactive 返回的对象可以直接通过属性访问,不需要 .value:
tsx
import { reactive, createApp } from 'vitarx'
function App() {
const state = reactive({ name: '张三', age: 25 })
return (
<div>
<p>姓名: {state.name}</p>
<p>年龄: {state.age}</p>
<button onClick={() => state.age++}>长大一岁</button>
</div>
)
}
createApp(App).mount('#app')reactive 的限制
- 只能传入对象类型(普通对象、数组、Map、Set 等),不能传入基本类型值
- 不能替换整个对象——因为
reactive返回的是代理对象,替换会丢失响应性
tsx
const state = reactive({ name: '张三' })
// ✅ 修改属性——有效
state.name = '李四'
// ❌ 替换整个对象——无效,会丢失响应性
state = { name: '王五' }如果你需要替换整个对象,应该使用 ref:
tsx
const state = ref({ name: '张三' })
// ✅ 替换整个对象——有效
state.value = { name: '王五' }ref vs reactive 选择建议
| 场景 | 推荐 | 原因 |
|---|---|---|
| 基本类型值(数字、字符串、布尔) | ref | reactive 不支持基本类型 |
| 需要替换整个对象 | ref | reactive 替换会丢失响应性 |
| 单一值状态 | ref | 语义更清晰 |
| 表单、配置等多属性对象 | reactive | 不需要 .value,代码更简洁 |
简单来说:不确定用哪个时,用 ref 就对了。
shallowRef
shallowRef 创建浅层响应式引用——只有 .value 的变化会触发更新,内部属性的变化不会触发:
tsx
import { shallowRef, createApp, watchEffect } from 'vitarx'
function App() {
const user = shallowRef({ name: '张三', age: 25 })
watchEffect(() => {
console.log('user 变化了:', user.value)
})
const changeAge = () => {
// ❌ 不会触发更新——修改内部属性
user.value.age = 26
}
const replaceUser = () => {
// ✅ 会触发更新——替换整个 .value
user.value = { name: '李四', age: 30 }
}
return (
<div>
<p>
{user.value.name} - {user.value.age}岁
</p>
<button onClick={changeAge}>修改年龄(不触发更新)</button>
<button onClick={replaceUser}>替换用户(触发更新)</button>
</div>
)
}
createApp(App).mount('#app')何时使用 shallowRef
- 包装大型数据结构,避免深层代理带来的性能开销
- 你只关心整体替换,不需要追踪内部属性变化
- 第三方库的实例对象(如 DOM 元素、Chart 实例等),不需要代理其内部
shallowReactive
shallowReactive 创建浅层响应式对象——只有根级属性是响应式的,嵌套对象不会被代理:
tsx
import { shallowReactive, watchEffect } from 'vitarx'
const state = shallowReactive({
name: '张三',
profile: { age: 25, city: '北京' }
})
watchEffect(() => {
console.log('state 变化了')
})
// ✅ 触发更新——修改根级属性
state.name = '李四'
// ❌ 不会触发更新——修改嵌套属性
state.profile.age = 26何时使用 shallowReactive
- 对象的嵌套层级很深,深层代理有性能问题
- 嵌套数据由第三方库管理,不需要框架代理
响应式解构陷阱
直接解构 reactive 对象会丢失响应性:
tsx
const state = reactive({ name: '张三', age: 25 })
// ❌ 解构后失去响应性
const { name, age } = state
// name 和 age 只是普通变量,state 变化时它们不会更新使用 toRefs 保持响应性
toRefs 将 reactive 对象的每个属性转为 ref,解构后仍然保持响应性:
tsx
import { reactive, toRefs, createApp } from 'vitarx'
function App() {
const state = reactive({ name: '张三', age: 25 })
// ✅ 使用 toRefs 解构,保持响应性
const { name, age } = toRefs(state)
return (
<div>
<p>姓名: {name}</p>
<p>年龄: {age}</p>
<button onClick={() => age.value++}>长大一岁</button>
</div>
)
}
createApp(App).mount('#app')WARNING
toRefs 返回的每个属性都是 ref,在 JavaScript 代码中需要通过 .value 访问。在模板中会自动解包。
完整示例
下面是一个综合使用各种响应式 API 的示例:
tsx
import { ref, reactive, shallowRef, toRefs, createApp } from 'vitarx'
function App() {
// ref:基本类型
const count = ref(0)
// reactive:多属性对象
const user = reactive({ name: '张三', age: 25 })
// shallowRef:大对象,只关心整体替换
const config = shallowRef({ theme: 'light', fontSize: 14 })
// toRefs:解构 reactive 保持响应性
const { name, age } = toRefs(user)
const toggleTheme = () => {
// shallowRef 需要替换整个 .value
config.value = {
...config.value,
theme: config.value.theme === 'light' ? 'dark' : 'light'
}
}
return (
<div style={{ padding: '20px' }}>
<h2>响应式状态示例</h2>
<div style={{ marginTop: '16px' }}>
<h3>ref 计数器</h3>
<p>当前计数: {count}</p>
<button onClick={() => count.value++}>+1</button>
</div>
<div style={{ marginTop: '16px' }}>
<h3>reactive 用户信息</h3>
<p>姓名: {name}</p>
<p>年龄: {age}</p>
<button onClick={() => age.value++}>长大一岁</button>
</div>
<div style={{ marginTop: '16px' }}>
<h3>shallowRef 配置</h3>
<p>主题: {config.value.theme}</p>
<button onClick={toggleTheme}>切换主题</button>
</div>
</div>
)
}
createApp(App).mount('#app')下一步
- 计算属性 — 学习如何派生自动更新的计算值