有时 Vue 的 reactivity 不符合需求,你需要重新渲染一个组件,或者可能只是想remove当前的 DOM 并 re-render。那么如何让 Vue 以正确的方式重新加载组件呢?
强制 Vue 重新渲染组件的最佳方法是在组件上设置一个:key
。当需要重新渲染组件时,只需更改键的值,Vue 就会重新渲染组件。
这称为Key-Changing Technique,这是一个非常简单的解决方案,让我们看看还有其他的解决方案吧。
在这篇文章中,我们将看看解决这个问题的方法,这些方法在 Vue 2 和 Vue 3 中都有效:
- ❌ 方式1:重新加载整个页面
- 🔪 方法2:使用v-if hack
- 👌 方法3:使用 Vue 内置的forceUpdate方法
- ✅ 方法4:使用 Key-Changing Technique 来刷新你的组件
❌ 方式1:重新加载整个页面
reload,这没啥好说了,也不建议这样做。
🔪 方法2:使用v-if hack
一个稍微好一点的解决方案是巧妙地使用v-if
指令。
在代码中,将在要重新加载的组件上添加指令:v-if
<template>
<MyComponent v-if="renderComponent" />
</template>
script setup
中可以添加 forceRerender
方法,然后使用 nextTick
:
import { nextTick, ref } from 'vue';
const renderComponent = ref(true);
const forceRerender = async () => {
// Remove MyComponent from the DOM
renderComponent.value = false;
// Wait for the change to get flushed to the DOM
await nextTick();
// Add the component back in
renderComponent.value = true;
};
Options API 的写法:
export default {
data() {
return {
renderComponent: true,
};
},
methods: {
async forceRerender() {
// Remove MyComponent from the DOM
this.renderComponent = false;
// Wait for the change to get flushed to the DOM
await this.$nextTick();
// Add the component back in
this.renderComponent = true;
}
}
};
执行流程:
- 最初
renderComponent
设置为true
,因此MyComponent
呈现 - 当调用
forceRerender
时,setrenderComponent
为false
-
MyComponent
组件消失,因为renderComponent
为false
- 然后 nextTick,
renderComponent
回到true
- 重新开始渲染一个刷新的实例
MyComponent
有两部分对于理解其工作原理很重要。
首先,next tick,才能看到变化。
在 Vue 中,一个 tick 是一个单一的 DOM 更新周期。Vue 将收集在同一个 tick 中进行的所有更新,并且在 tick 结束时,它将根据这些更新更新渲染到 DOM 中的内容。
如果没等到next tick,更新renderComponent
将自行取消,什么都不会改变。
其次,当第二次渲染时, Vue 将创建一个全新的组件,因为 Vue 会销毁第一个并创建一个新的。这意味着newMyComponent
将像往常一样经历它的所有生命周期—— created
、mounted
等等。
这可行,但这不是一个很好的解决方案,我称之为 hack,因为我们正在围绕 Vue 希望我们做的事情进行 hack。
👌 方法3:使用 Vue 内置的forceUpdate方法
这是解决这个问题的两种最佳方法之一,这两种方法都得到了 Vue 的官方支持。
通常,Vue 会通过更新视图来响应依赖项的变化。但是,当调用forceUpdate
时,可以强制进行更新,即使实际上没有任何依赖项发生更改。
这绕过了Vue的reactivity,这就是为什么不推荐将其作为解决方案的原因。
但有时,Vue的reactivity 会令人困惑,有时候我们认为 Vue 会对某个属性或变量的变化做出反应,但事实并非如此。如果使用的是 Vue 2,在某些情况下,Vue 的反应系统根本不会检测到任何更改。但是 Vue 3 有一个更强大的基于代理的反应系统,基本不会遇到这些相同的问题。
以下是forceUpdate
使用 Options API 调用的方法:
export default {
methods: {
methodThatForcesUpdate() {
this.$forceUpdate();
}
}
}
在 Vue 3 中使用 Composition API 调用:
import { getCurrentInstance } from 'vue';
const methodThatForcesUpdate = () => {
const instance = getCurrentInstance();
instance.proxy.forceUpdate();
};
重要提示:调用forceUpdate
只会强制视图重新渲染。
✅ 方法4:使用 Key-Changing Technique 来刷新你的组件
在许多情况下,会强制刷新组件的需求。为了以正确的方式做到这一点,我们将提供一个key
属性,以便 Vue 知道特定的组件与特定的数据片段相关联。如果 key 保持不变,它不会改变组件,但如果 key 确实改变了,Vue 知道它应该销毁旧组件并创建一个新组件。
这是强制 Vue 刷新组件的最佳方式(在我看来)。
我们向组件添加一个key
属性,然后在需要重新渲染组件时更改该键。
这是一个非常基本的方法script setup
:
<template>
<MyComponent :key="componentKey" />
</template>
import { ref } from 'vue';
const componentKey = ref(0);
const forceRerender = () => {
componentKey.value += 1;
};
Options API 的写法:
export default {
data() {
return {
componentKey: 0,
};
},
methods: {
forceRerender() {
this.componentKey += 1;
}
}
}
每次forceRerender
调用时componentKey
都会改变。发生这种情况时,Vue 将知道它必须销毁组件并创建一个新组件。你得到的是一个子组件,它将重新初始化自身并“重置”它的状态,强制刷新并重新渲染组件。
请记住,如果您发现自己需要强制 Vue 重新渲染组件,可能有更好的方法。
但是,如果确实需要重新渲染某些内容,请选择 Key-Changing Technique。