Vue 里”刷新组件”是个模糊需求。要重新 render 一次?重新执行 setup 拉数据?还是彻底把内部 state 归零?不同目标要用不同手段。
四种方式的差别
| 手段 | 会销毁组件 | 会重新执行 setup | 会重置 state |
|---|---|---|---|
改 :key | ✅ | ✅ | ✅ |
$forceUpdate() | ❌ | ❌ | ❌ |
| 重新赋值 ref/reactive | ❌ | ❌ | 只改被赋值的字段 |
v-if 切换 | ✅ | ✅ | ✅ |
场景一:改 :key(推荐)
想”完全刷新”一个组件(比如切换 tab 后要重新拉数据、重置表单):
<template>
<UserForm :key="refreshKey" />
<button @click="refresh">重新加载</button>
</template>
<script setup>
import { ref } from "vue";
const refreshKey = ref(0);
const refresh = () => refreshKey.value++;
</script>
refreshKey 一变,Vue 视这是新组件——旧的销毁、新的挂载、setup() 重跑、生命周期钩子从 onMounted 全走一遍。
场景二:$forceUpdate(几乎用不到)
只想强制重新 render,不动 state:
import { getCurrentInstance } from "vue";
const instance = getCurrentInstance();
instance.proxy.$forceUpdate();
适用场景其实很窄——一般是把不响应的对象塞进模板,或者用了第三方非响应库。只要还能改成响应式,就别用 $forceUpdate。
场景三:v-if 切换
粗暴但清晰:
<template>
<UserForm v-if="show" />
</template>
<script setup>
const show = ref(true);
async function reset() {
show.value = false;
await nextTick();
show.value = true;
}
</script>
等价于改 key,用于”父组件不方便传 key”的场景。
数据变了但界面不更新
Vue3 大多数场景响应式自动生效,出问题基本是下面几种:
1. 解构丢了响应式
const state = reactive({ count: 1 });
const { count } = state; // count 是快照,不再响应
改成 toRefs:
const { count } = toRefs(state);
count.value++; // 会触发更新
2. 用了 shallowRef 但改的是深层
const data = shallowRef({ a: 1 });
data.value.a = 2; // 不会更新
手动触发:
import { triggerRef } from "vue";
triggerRef(data);
或者直接换整个引用:
data.value = { ...data.value, a: 2 };
3. 用了 markRaw / Object.freeze
这些对象上禁用响应式,任你怎么改视图都不动。
4. Vue 2 的经典坑(Vue 3 无此问题)
this.arr[index] = value不触发 → 用this.$set或splice(index, 1, value)this.obj.newKey = 1不触发 → 用this.$set(this.obj, "newKey", 1)
nextTick:拿最新 DOM
修改数据后立刻读 DOM 大小、滚动位置:
import { nextTick } from "vue";
count.value++;
await nextTick();
console.log(el.value.scrollHeight); // 这时才是新高度
一句话总结
“重置组件”改 :key、“只重画”用 $forceUpdate(且大概率你不该用它)、“数据不更新”先查是不是解构丢了响应式。
