Vue3 响应式核心 API
Vue3 响应式核心 API
一、ref:基础类型的响应式
ref 是 Vue3 中最常用的响应式 API 之一,专门用于将基础类型(Number、String、Boolean 等) 转为响应式数据;同时也支持处理引用类型(Object、Array),但底层会自动用 reactive包裹。
1. 核心特性
- 本质是一个“响应式容器”:把一个简单类型包装成一个对象,使它可以被追踪(响应式)。ref 返回的是一个包含 .value 属性的对象,模板中直接使用,操作时需要访问.value
- 支持基础类型与引用类型:基础类型直接存于 value,引用类型会被 reactive 转为响应式对象
2. 实战示例
<template><div><!-- 模板中使用 ref 数据,无需 .value --><p>计数器:{{ count }}</p><p>用户名:{{ user.name }}</p><button @click="updateData">更新数据</button></div>
</template><script setup>
import { ref, unref } from 'vue'// 1. 基础类型转响应式
const count = ref(0) // 初始值为 0,返回 ref 对象
// 2. 引用类型转响应式(底层自动用 reactive 处理)
const user = ref({ name: '张三', age: 20 })// 修改 ref 数据:需通过 .value
const updateData = () => {count.value += 1 // 基础类型修改user.value.name = '李四' // 引用类型修改(.value 后等同于 reactive 对象)}
</script>
二、reactive:引用类型的响应式代理
reactive 用于将引用类型(Object、Array) 转为响应式数据,底层通过 ES6 Proxy 创建对象的代理,拦截数据的读写操作,从而实现响应式更新。
1. 核心特性
- 仅支持引用类型:若传入基础类型,会直接警告(需用 ref`处理基础类型);
- 无需 .value 访问:创建的响应式对象可直接读写属性(如 user.name),无需像 ref 那样通过 .value;
- 深层响应式:默认对嵌套对象/数组进行“深度代理”,无论嵌套多少层,修改内部属性都会触发视图更新。
2. 实战示例
<template><div><p>用户信息:{{ user.name }}({{ user.age }}岁)</p><p>爱好:{{ hobbies.join(', ') }}</p><button @click="updateData">更新数据</button></div>
</template><script setup>
import { reactive } from 'vue'// 1. 对象转响应式
const user = reactive({name: '张三',age: 20,address: { city: '北京' } // 嵌套对象,默认深层响应式
})// 2. 数组转响应式
const hobbies = reactive(['篮球', '游戏'])// 直接修改属性,无需 .value
const updateData = () => {user.name = '李四' user.address.city = '上海' hobbies.push('读书')
}
</script>
3. 注意事项
- 避免“解构丢失响应式”:直接解构 reactive对象的属性(如 const { name } = user),解构出的 name 会变成普通值,若需解构需用 toRefs`辅助;
- 当数据存在多层嵌套时,reactive的深层响应式特性可简化代码,无需手动处理嵌套层级的响应式。
三、shallowReactive:浅层响应式
shallowReactive是 reactive的“浅层版本”,仅对顶层属性进行响应式代理,嵌套属性不会转为响应式(即“浅代理”)。
1. 核心特性
- 浅层响应式:仅顶层属性修改会触发视图更新,嵌套属性修改无响应;
- 适用引用类型:与 reactive 一致,仅支持引用类型,不支持基础类型。
2. 实战示例
<template><div><p>{{ shallowObj.topProp }}({{ shallowObj.nestedProp.val }})</p><p>{{ deepObj.topProp }}({{ deepObj.nestedProp.val }})</p><button @click="updateData">更新嵌套属性</button></div>
</template><script setup>
import { shallowReactive, reactive } from 'vue'// 1. shallowReactive:仅顶层属性响应式
const shallowObj = shallowReactive({data: '顶层值',anotherData: { val: '111' }
})// 2. reactive:深层响应式
const deepObj = reactive({data: '顶层值',anotherData: { val: '111 }const updateData = () => {// 修改 shallowObj 无响应(不更新)shallowObj.nestedProp.val = '222'// 修改 deepObj :有响应(更新)deepObj.nestedProp.val = '222'
}
</script>
四、readonly:只读响应式
readonly 用于创建一个只读的响应式对象,无论是 ref`还是 reactive 对象,经过 readonly 处理后,其属性无法被修改,但仍保持响应式(若原对象更新,只读对象也会同步更新)。
1. 核心特性
- 只读不可改:无论是顶层属性还是嵌套属性,都无法直接修改;
- 保持响应式:若原响应式对象(如 reactive/ref)更新,readonly`对象会同步更新视图;
- 深层只读:默认对嵌套对象进行“深层只读处理”
2. 实战示例
<template><div><p>只读用户信息:{{ readonlyUser.name }}({{ readonlyUser.age }}岁)</p><p>原用户信息:{{ user.name }}({{ user.age }}岁)</p><button @click="updateData">尝试修改</button></div>
</template><script setup>
import { reactive, readonly } from 'vue'// 1. 创建原响应式对象
const user = reactive({ name: '张三', age: 20 })
// 2. 转为只读响应式对象
const readonlyUser = readonly(user)const updateData = () => {// 尝试修改 readonly 对象:开发环境警告,修改无效readonlyUser.name = '李四' // 无效(只读拦截)// 修改原对象:readonly 对象会同步更新(保持响应式)user.name = '李四' // 原对象修改后,readonlyUser 视图同步更新
}
</script>
3. 适用场景
- 保护“不可修改的全局配置”:如项目的主题配置、接口基础地址等,用 readonly 防止误修改;
- 组件间传递“只读数据”:父组件向子组件传递数据时,若不希望子组件修改数据,可将数据用 readonly`包装后传递,避免子组件破坏父组件状态(遵循单向数据流)。
五、watchEffect:“自动追踪依赖”的响应式副作用
watchEffect 是 Vue3 中用于处理“响应式副作用”的 API,它会自动追踪函数内部的响应式数,当这些数据更新时,函数会重新执行。相比 watch,watchEffect 无需手动指定监听的数据源,更简洁。
1. 核心特性
- 处理所有输入框作为监听源,无需手动列举所有输入框作为监听源;
2. 实战示例(基础用法 + 清理副作用)
<template><div><p>计数器:{{ count }}</p><button @click="count++">+1</button></div>
</template><script setup>
import { ref, watchEffect } from 'vue'const count = ref(0)// 1. 基础用法:自动追踪 count 依赖
watchEffect(() => {console.log('count 更新了:', count.value) // 初始执行一次,count 变化时再执行
})// 2. 清理定时器
watchEffect((onCleanup) => {// 每次 count 更新时,先执行清理函数,再执行副作用const timer = setTimeout(() => {console.log('count 延迟打印:', count.value)}, 1000)onCleanup(() => {clearTimeout(timer) // 清除上一次的定时器,避免多个定时器叠加})
})
</script>
六、toRefs:创建结构后的响应式连接
将响应式对象转换为普通对象,其中结果对象的每个属性都是指向原始对象相应属性的 ref,保持对其源的响应式连接。
1. 核心特性
- 与toRef相似,可以用来为响应式对象上的多个属性新创建多个 ref ,从而保持对其原来属性的响应式连接。
2. 实战示例
<template><div>id:{{id}}</div><div>name:{{name}}</div>
</template>
setup() {const state = reactive({id: 1,name: 'front-refiend'});return {...toRefs(state)};
}
//原理
function toRefs(object) {const ret = {};for (const key in object) {ret[key] = toRef(object, key);}return ret;
}
3.ref 与 reactive 解构问题:
- reactive直接解构会丢失响应式,需用 toRefs(如 const { name } = toRefs(user));
- ref 解构时,若用 const { value: countVal } = count,countVal是非响应式的,需直接使用 toRef。