问题来源:
在编写动态组件渲染时:
<div>
<button @click="handleChangeTag('C')">组件C</button>
<button @click="handleChangeTag('D')">组件D</button>
<button @click="handleChangeTag('E')">组件E</button>
<component :is="componentTag"></component>
</div>
const componentTag = ref(childC) //如果用ref,此处被警告节省性能开销
const handleChangeTag = (idx) => {
componentTag.value = (idx === 'C') ? childC : (idx === 'D' ? childD : childE)
}
问题分析:
这个Vue警告信息指出,Vue接收到了一个被标记为响应式对象的组件。在Vue 3中,响应式系统是通过Proxy来实现的,它会拦截对象属性的访问和修改。当你将一个组件实例或类似对象转换为响应式对象时,Vue需要额外的工作来追踪这些属性的变化,这可能导致不必要的性能开销。
问题解决:
只需定义componentTag的时候用shallowRef来定义即可:
const componentTag = shallowRef(childC)
扩展:
Vue提供了markRaw
函数和shallowRef
来处理这种情况:
-
markRaw:
markRaw
函数用于标记一个对象,使其不会被Vue转换为响应式对象。如果你有一个对象,你不希望Vue对其进行响应式处理,可以使用markRaw
。import { markRaw } from 'vue'; const rawObject = { /* ... */ }; const rawComponent = markRaw(rawObject);
使用
markRaw
后,rawObject
不会被Vue响应式系统处理。 -
shallowRef:
shallowRef
与ref
类似,但它创建的是一个浅响应式引用。这意味着只有引用的值是响应式的,而值的内部结构不会被Vue追踪为响应式。这适用于当你需要响应式引用一个对象,但不需要Vue追踪对象内部属性的变化时。import { shallowRef } from 'vue'; const shallowObject = shallowRef({ /* ... */ });
在这个例子中,
shallowObject
是一个响应式引用,但只有它指向的对象是响应式的,对象内部的属性变化不会被追踪。
解决警告的方法:
- 如果你有一个组件或对象,它不应该被Vue追踪为响应式,使用
markRaw
来标记它。 - 如果你只需要对象的引用是响应式的,而对象内部不需要响应式处理,使用
shallowRef
来创建这个引用。
使用这些方法可以避免不必要的性能开销,并确保Vue的响应式系统更高效地运行。