readonly 与 shallowReadonly、toRaw 与 markRaw、customRef、provide 与 injectVue 3 的响应式系统为开发者提供了更强大的工具,其中包括 readonly、shallowReadonly、toRaw、markRaw 以及 customRef 等 API,进一步增强了对响应式状态的控制。同时,provide 和 inject 为组件间的依赖注入提供了简洁的方案。在本文中,我们将深入探讨这些新特性,了解它们的使用场景和方法。
readonly 与 shallowReadonlyreadonly?readonly 是 Vue 3 提供的一个 API,用于创建只读的响应式对象。该对象本身是响应式的,但无法修改其属性值。任何修改操作都会被忽略,并且在开发环境下会发出警告。
import { readonly, reactive } from 'vue';
export default {
setup() {
const state = reactive({ name: 'John', age: 30 });
const readonlyState = readonly(state); // 创建只读副本
readonlyState.name = 'Doe'; // 修改不会生效,且在开发模式下会发出警告
return { readonlyState };
}
};shallowReadonly?shallowReadonly 是 readonly 的浅层版本,它只会使第一层属性成为只读,而不对嵌套的对象进行处理。
import { shallowReadonly } from 'vue';
export default {
setup() {
const state = shallowReadonly({
user: { name: 'Alice', age: 25 },
isAdmin: true
});
state.isAdmin = false; // 修改无效,且在开发环境下会发出警告
state.user.name = 'Bob'; // 修改有效,因为是浅层只读
return { state };
}
};readonly:用于确保数据不会被误改,适合在传递数据到子组件时防止子组件直接修改状态。shallowReadonly:用于对第一层属性进行只读保护,同时允许嵌套对象的属性可变。toRaw 与 markRawtoRaw?toRaw 是一个 Vue 3 的 API,它用于获取 reactive 或 readonly 对象的原始对象。通过 toRaw,我们可以绕过 Vue 的响应式系统,获取未被代理的原始数据。
import { reactive, toRaw } from 'vue';
export default {
setup() {
const state = reactive({ name: 'John', age: 30 });
const rawState = toRaw(state); // 获取原始对象
console.log(rawState); // 打印未代理的原始数据
return { state, rawState };
}
};markRaw?markRaw 用于将对象标记为“非响应式对象”,即使将其传入 reactive 或 readonly,也不会被转换为响应式数据。
import { reactive, markRaw } from 'vue';
export default {
setup() {
const rawData = markRaw({ foo: 'bar' });
const state = reactive({
data: rawData // 这里的 rawData 不会变成响应式
});
state.data.foo = 'baz'; // 不会触发响应式更新
return { state };
}
};toRaw:用于调试和处理一些性能问题,比如当不需要响应式时,直接操作原始数据。markRaw:适合不希望某些对象成为响应式数据的场景,尤其是第三方库的数据对象或大型数据结构时,可以防止不必要的性能开销。customRefcustomRef?customRef 是 Vue 3 提供的一个用于创建自定义 ref 的 API。它允许开发者自定义 ref 的行为,控制数据的获取和设置时触发的响应式更新。
customRef通过 customRef,我们可以完全控制 ref 的依赖收集和更新过程。例如,可以使用它来实现防抖输入框的逻辑:
import { customRef } from 'vue';
function useDebouncedRef(value, delay = 300) {
let timeout;
return customRef((track, trigger) => {
return {
get() {
track(); // 依赖收集
return value;
},
set(newValue) {
clearTimeout(timeout);
timeout = setTimeout(() => {
value = newValue;
trigger(); // 触发更新
}, delay);
}
};
});
}
export default {
setup() {
const searchQuery = useDebouncedRef('', 500); // 500ms 防抖
return { searchQuery };
}
};ref 行为:当需要在 ref 的设置或获取过程中加入额外逻辑时(如防抖、节流等)。provide 与 injectprovide 与 inject?provide 和 inject 是 Vue 3 中的依赖注入机制,用于在祖先组件和后代组件之间传递数据。相比于通过 props 逐级传递数据,provide 和 inject 更加简洁灵活,适用于跨层级组件通信。
provide 传递数据在祖先组件中,使用 provide 来提供数据:
import { provide } from 'vue';
export default {
setup() {
const theme = 'dark';
provide('theme', theme); // 提供名为 'theme' 的数据
return {};
}
};inject 接收数据在后代组件中,使用 inject 来获取数据:
import { inject } from 'vue';
export default {
setup() {
const theme = inject('theme'); // 获取 'theme' 数据
return { theme };
}
};provide 和 inject 的使用场景props 和 $emit 时,provide 和 inject 提供了一种轻量级的解决方案。provide 注入全局服务或配置。通过本文的学习,你应该掌握了以下关键点:
readonly 和 shallowReadonly:了解了如何创建只读的响应式对象,并学会在不同场景下选择全局或浅层只读。toRaw 和 markRaw:学会了如何获取响应式对象的原始数据,以及如何标记对象为非响应式数据,以避免性能问题或不必要的响应式开销。customRef:掌握了如何自定义 ref 的行为,特别是在需要对响应式数据进行精细控制的场景下。provide 和 inject:理解了依赖注入的机制,学会在跨层级组件之间传递数据,提升了数据共享的灵活性。Vue 3 的这些新功能为开发者提供了更加灵活、可控的工具,帮助我们更好地管理和优化应用中的状态与数据传递。在接下来的博客中,我们将继续探索 Vue 3 的更多高级特性和实际应用场景。如果你有任何疑问或需要进一步讨论,欢迎在评论区留言。感谢你的阅读,期待在下一篇博客中继续与大家分享更多 Vue.js 和 Vue 3 的开发技巧与经验!