您现在的位置是:首页 >学无止境 >Vue3 性能优化之shallowReactive和shallowRef网站首页学无止境
Vue3 性能优化之shallowReactive和shallowRef
Vue的响应式系统默认是深度的。虽然这让状态管理变得更直观,但在数据量巨大时,深度响应性也会导致不小的性能负担,因为每个属性访问都将出发代理的依赖追踪。好在这种性能负担通常只有在处理超大型数组或层级很深的对象时,例如:一次渲染需要访问100,000+个属性时,才会变得比较明显。因此,他只会影响少数特定的场景。
vue确实也为此提供了一种解决方案,通过使用 shallowRef() 和 shallowReactive() 来绕开深度响应。
浅层和深层的对比:
1.浅层(shallow)
定义:只对数据结构的最外层进行操作或跟踪。对于嵌套的属性或者对象,不会递归地追踪其内部的变化。
应用:在需要优化性能时,减少不必要的深层次数据追踪,比如:shallowRef 和 shallowReactive 提供了这种功能。
优点:提高性能,减少Vue 内部的深度响应式追踪,特别是在数据结构复杂时效果显著
缺点:无法自动跟踪嵌套对象或数组内部的变化,需要手动处理或选择其他方式
2.深层(deep)
定义:对数据结构的所有层级都进行操作或跟踪。递归地追踪嵌套的对象或数组中的所有属性。
应用:在需要全面跟踪数据变化时,例如使用 reactive 进行深层响应式追踪。。
优点:能够自动跟踪嵌套的所有属性变化,确保数据的每一层都响应式。
缺点:性能开销较大,特别是在数据结构非常复杂或庞大时。
浅层式API(shallowRef 和 shallowReactive)创建的状态只在其顶层是响应式的,对所有深层的对象不会做任何处理,这使得对深层级属性的访问变得更快,但代价是,我们现在必须将所有深层级对象视为不可变的,并且只能通过替换整个根状态来触发更新:
接下来详细介绍下这两个浅层式AP:
shallowRef:
功能:创建一个浅层响应式引用,只对引用值的第一层数据变化进行响应式处理,不会递归追踪嵌套对象的变化
使用场景:
- 当处理复杂数据结构,但只关心顶层数据的变化时,使用 shallowRef 可以避免不需要的深层次响应式追踪
- 适用于需要优化性能的场景,尤其是当数据结构非常复杂或庞大时
const user = shallowRef({
name: 'Alice',
age: 30,
address: {
city: 'San Francisco',
state: 'CA'
}
});
watchEffect(()=>{
console.log(user.value)
})
user.value.name = 'Bob'; // 不触发响应式更新
user.value = { name: 'Bob', age: 35 }; // 触发响应式更新
triggerRef:是一个用于触发响应式更新的工具,主要用于与 shallowRef 配合使用。triggerRef 方法用于强制触发对shallowRef的响应式更新。当你需要对shallowRef的内层属性进行操作,并希望这些操作触发响应式更新时,可以使用triggerRef
const shallow = shallowRef({
greet: 'Hello, world'
})
// 触发该副作用第一次应该会打印 "Hello, world"
watchEffect(() => {
console.log(shallow.value.greet)
})
// 这次变更不应触发副作用,因为这个 ref 是浅层的
shallow.value.greet = 'Hello, universe'
// 打印 "Hello, universe"
triggerRef(shallow)
customRef:是一个Vue3 提供的一个高级API,用于创建自定义的响应式引用。它允许显式的控制以来追踪和触发响应的逻辑,从而实现更复杂的响应式方式。
基本概念:
customRef 接收一个函数作为参数,该函数返回一个包含 get 和 set 方法的对象。get 方法在读取值时调用,set 方法在设置值时调用。通过track 和 trigger 函数,可以手动控制依赖追踪和响应式更新。
import { customRef } from 'vue';
function createCustomRef(value) {
return customRef((track, trigger) => {
return {
get() {
track(); // 追踪依赖
return value;
},
set(newValue) {
value = newValue;
trigger(); // 触发更新
}
};
});
}
常见使用场景:
1. 防抖Ref:防抖功能可以延迟响应式更新,适用于输入框的搜索场景
function useDebouncedRef(value, delay = 200) {
let timeout;
return customRef((track, trigger) => {
return {
get() {
track();
return value;
},
set(newValue) {
clearTimeout(timeout);
timeout = setTimeout(() => {
value = newValue;
trigger();
}, delay);
}
};
});
}
2.节流Ref:节流功能可以限制响应式更新的频率,适用于滚动事件
function useThrottledRef(value, delay = 200) {
let lastTriggerTime = 0;
return customRef((track, trigger) => {
return {
get() {
track();
return value;
},
set(newValue) {
const now = Date.now();
if (now - lastTriggerTime >= delay) {
value = newValue;
lastTriggerTime = now;
trigger();
}
}
};
});
}
3.验证Ref:在设置值时进行验证,确保值符合特定条件
function useValidatedRef(value, validator) {
return customRef((track, trigger) => {
return {
get() {
track();
return value;
},
set(newValue) {
if (validator(newValue)) {
value = newValue;
trigger();
} else {
console.warn('Invalid value:', newValue);
}
}
};
});
}
4.异步Ref:用于异步加载数据,例如从API获取数据
function useAsyncRef(getter) {
let value = null;
let isLoading = true;
return customRef((track, trigger) => {
getter().then((data) => {
value = data;
isLoading = false;
trigger();
});
return {
get() {
track();
return { value, isLoading };
},
set() {
throw new Error('Async ref is readonly');
}
};
});
}
高级应用场景
1. 持久化 Ref:将值存储到 localStorage 中。
function useLocalStorageRef(key, defaultValue) {
const storedValue = JSON.parse(localStorage.getItem(key) || 'null');
let value = storedValue !== null ? storedValue : defaultValue;
return customRef((track, trigger) => {
return {
get() {
track();
return value;
},
set(newValue) {
value = newValue;
localStorage.setItem(key, JSON.stringify(newValue));
trigger();
}
};
});
}
2. 格式化 Ref:对值进行格式化和解析
function useFormattedRef(value, formatter, parser) {
return customRef((track, trigger) => {
return {
get() {
track();
return formatter(value);
},
set(newValue) {
value = parser(newValue);
trigger();
}
};
});
}
最佳实践:
- 性能优化:避免不必要的触发,仅在值真正改变时调用trigger
- 错误处理:在get 和set 方法中添加错误处理逻辑
- 避免过度使用:对于简单的值,直接使用ref 即可
shallowReactive:
功能:创建一个浅层响应式对象,只对对象的第一层属性进行响应式处理,不会递归的将嵌套对象的熟悉也变成响应式
使用场景:
- 当处理复杂嵌套对象时,但只关心对象的第一层属性变化,使用shallowReactive可以显著减少性能开销
- 避免对嵌套对象的深层次属性进行不必要的响应式追踪,从而优化性能
const user = shallowReactive({
name: 'John',
age: 25,
address: {
city: 'New York',
country: 'USA'
}
});
user.name = 'Jane'; // 触发响应式更新
user.address.city = 'Los Angeles'; // 不触发响应式更新
总结:
性能优化:shallowRef 和 shallowReactive 通过减少不必要的深度响应式追踪,显著提高了性能
使用场景:当不需要嵌套对象的深层次属性进行响应式处理时,使用它们可以避免不必要的性能开销
注意事项:如果需要对嵌套属性进行响应式处理,则应使用reactive 或 ref




QT多线程的5种用法,通过使用线程解决UI主界面的耗时操作代码,防止界面卡死。...
U8W/U8W-Mini使用与常见问题解决
stm32使用HAL库配置串口中断收发数据(保姆级教程)
分享几个国内免费的ChatGPT镜像网址(亲测有效)
Allegro16.6差分等长设置及走线总结