shallowReactive和shallowRef
1. shallowReactive
- 用于创建浅层响应式对象,仅处理第一层属性的响应式。当修改嵌套属性时不会触发更新
- 适用于外层属性变化频繁但内层结构稳定的情况。如分页组件的查询参数对象
- 使用shallowReactive可以提升性能
2. shallowRef
- 主要用于基本数据类型,或替换整个对象而不是修改内部属性。
- 当需要替换整个对象而不是修改其内部属性时,使用shallowRef更合适,如重置表单对象时整体替换
- 可以避免不必要的深度响应式处理,提高性能,因为它不会追踪对象内部的变化,只关心.value引用变化
3.shallowReactive 和 shallowRef 都是针对特定场景设计的浅层响应式 API
const state = shallowReactive({ a: 1, nested: { b: 2 } });
state.a = 2; // 触发更新
state.nested.b = 3; // 不触发更新
const refObj = shallowRef({ x: 1 });
refObj.value = { x: 2 }; // 触发更新
refObj.value.x = 3; // 不触发更新
4. 局限性:嵌套属性修改需通过触发外层变更(如对象替换)或配合其他 API(如 triggerRef)实现更新
readonly 与 shallowReadonly
1. readonly
- 创建一个深度只读的代理对象,所有嵌套属性均不可修改,任何修改尝试会触发警告并阻止操作
- 不可变全局配置:如应用的主题配置、权限规则等,确保数据不被意外修改
- 传递只读数据:父组件传递数据给子组件时,防止子组件修改原始数据
const state = readonly({ a: 1, nested: { b: 2 } });
state.a = 2; // 触发警告,不生效
state.nested.b = 3; // 触发警告,不生效
2. shallowReadonly
- 仅对对象的第一层属性设为只读,嵌套对象的属性仍可修改
- 部分只读需求:仅需保护对象顶层属性,如分页参数中的页码(page)不可修改,但嵌套的查询条件(query)允许变动
- 性能敏感场景:当数据嵌套层级较深时,避免递归代理带来的性能损耗
const state = shallowReadonly({ a: 1, nested: { b: 2 } });
state.a = 2; // 触发警告,不生效
state.nested.b = 3; // 可正常修改
3. 开发环境警告:修改 readonly 或 shallowReadonly 对象的只读属性时,控制台会输出警告,生产环境无警告
4. 与响应式数据结合:两者可接受 reactive 或 ref 创建的响应式对象,返回只读代理
toRaw 与 markRaw
1. toRaw
- 作用:获取由 reactive、readonly 等创建的响应式代理对象的原始非代理版本,解除响应式追踪
- 返回值:返回原始对象,对该对象的修改不会触发视图更新
- 性能优化:处理大型数据时,避免响应式追踪的开销(如大数据计算或遍历)
- 第三方库集成:向不支持响应式系统的外部库传递原始数据
- 仅作用于响应式代理对象,对普通对象调用会直接返回自身
- 获取的原始对象不应长期引用,否则可能引发数据不一致问题
const state = reactive({ list: [/* 大量数据 */] });
const rawList = toRaw(state.list); // 避免响应式代理的性能损耗
2. markRaw
- 作用:标记一个对象,使其永远不会被转换为响应式代理,即使被嵌套在响应式对象中
- 返回值:返回被标记的原始对象本身
- 静态数据标记:如第三方类库实例、工具类对象或固定配置数据
- 不可变数据渲染:渲染大型不可变列表时,跳过响应式转换以提升性能
- 被标记对象的嵌套属性仍可能被响应式系统追踪(除非手动递归标记)
- 修改被标记对象的属性不会触发更新,但直接替换整个对象会触发响应
const helper = markRaw(new HelperClass()); // 工具类无需响应式
const state = reactive({ helper });
customRef
- customRef 是 Vue3 提供的用于创建自定义响应式引用的 API,允许开发者显式控制依赖追踪(track)和更新触发(trigger)
- 适用于需要精细化控制响应式逻辑的场景
customRef
默认不自动追踪依赖,需手动调用track()
和trigger()
以提升性能- 防抖示例
import { customRef } from 'vue';
function debouncedRef(initialValue, delay = 500) {
let timeout;
return customRef((track, trigger) => ({
get() {
track(); // 追踪依赖
return initialValue;
},
set(newValue) {
clearTimeout(timeout);
timeout = setTimeout(() => {
initialValue = newValue;
trigger(); // 触发更新
}, delay);
}
}));
}
const keyword = debouncedRef(''); // 使用防抖 Ref
- 异步操作(如接口请求)完成后触发更新示例
function asyncRef(url) {
let data = null;
return customRef((track, trigger) => ({
get() {
track();
return data;
},
async set() {
const res = await fetch(url);
data = await res.json();
trigger();
}
}));
}
provide 与 inject
- provide 与 inject 是 Vue3 提供的跨层级组件通信 API,允许祖先组件向任意后代组件直接传递数据或方法,无需通过中间组件逐层传递 Props
- Vue3 在组件实例中通过 _provided 属性存储 provide 提供的数据。 inject 从当前组件向上递归查找祖先组件的 _provided 属性,匹配键名获取数据
- provide:在父组件中定义需提供的数据或方法
// 父组件(祖先)
import { ref, provide } from 'vue';
setup() {
//默认情况下provide 提供的数据非响应式,需通过 ref 或 reactive 包装以实现响应式更新
const message = ref('Hello');
provide('messageKey', message); // 提供响应式数据
}
- inject:在后代组件中接收注入的数据或方法
// 后代组件(如孙子)
import { inject } from 'vue';
setup() {
const message = inject('messageKey'); // 接收数据
return { message };
}
响应式数据的判断
isRef
判断一个值是否为ref
定义的响应式数据
import { ref, isRef } from 'vue';
const count = ref(0);
console.log(isRef(count)); // true
- isReactive 检查对象是否由 reactive 创建的响应式代理
import { reactive, isReactive } from 'vue';
const obj = reactive({ name: 'Vue' });
console.log(isReactive(obj)); // true
const plainObj = { name: 'Vue' };
console.log(isReactive(plainObj)); // false
- isReadonly 判断对象是否由 readonly 创建的只读代理
import { reactive, readonly, isReadonly } from 'vue';
const original = reactive({ name: 'Vue' });
const copy = readonly(original);
console.log(isReadonly(copy)); // true
- isProxy 检测对象是否由 reactive 或 readonly 创建的代理(包括 shallowReactive 和 shallowReadonly)
import { reactive, isProxy } from 'vue';
const obj = reactive({ name: 'Vue' });
console.log(isProxy(obj)); // true
const obj = readonly(reactive({ name: 'Vue' }));
console.log(isProxy(obj)); // true
console.log(isReactive(obj)); // false