响应式状态

响应式状态是 Vitarx 的核心——当数据变化时,界面会自动更新。本页介绍如何使用 refreactive 创建响应式数据。

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 选择建议

场景推荐原因
基本类型值(数字、字符串、布尔)refreactive 不支持基本类型
需要替换整个对象refreactive 替换会丢失响应性
单一值状态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 保持响应性

toRefsreactive 对象的每个属性转为 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')

下一步