我在这里为我问题做了一些沙盒代码:
https://codesandbox.io/s/clever-zeh-kdff1z
<template>
<div v-if="started">
<HelloWorld :msg="msg" @exit="exit" @remake="remake" />
</div>
<button v-if="!started" @click="started = !started">start</button>
</template>
<script>
import HelloWorldVue from "./components/HelloWorld.vue";
export default {
name: "App",
components: {
HelloWorld: HelloWorldVue,
},
data() {
return {
started: false,
msg: "Hello Vue 3 in CodeSandbox!",
};
},
methods: {
exit() {
this.started = false;
},
remake() {
this.msg = this.msg + 1;
//this code should recreate our child but...
this.exit();
this.started = true;
// setTimeout(() => {
// this.started = true;
// });
},
},
};
</script>
所以!我们有两个组件父组件和子组件。这个想法很简单-我们在父组件中有一个flag变量。我们有一个v-if语句-隐藏/显示一个元素取决于flag值“false”或“true”。在我们切换flag之后-子组件应该被重新创建。这就是这个想法。很简单。
在我们的父页面中,我们有一个按钮,它将把flag变量设置为“true”,我们的子页面将被创建并显示在我们的页面上。
好了。现在我们有两个按钮在我们的孩子。一个按钮是“退出”,这是发射一个事件,使标志变量的父将设置为“假”和元素将从我们的页面消失(它将被销毁)。工程作为魅力。好。
第二个按钮“remake”。它会发出事件,这样flag变量就会被切换(关闭然后打开)。很简单。我们设置为“false”,我们设置为“true”。所以当前的孩子应该会消失,然后立即会创建一个新的。
但是现在我们面临的问题是!好的,当前的子进程仍然在这里,没有任何重新创建,它只是更新当前的一个...所以在子进程中我检查了我们的生命周期钩子-通过console.log函数创建和卸载。第二个按钮不触发它们。开始-〉退出-〉开始!=开始-〉重做。
所以有没有人能解释一下为什么会发生这种事?我想不通。
有趣的是,在我的演示中有一些异步代码注解。如果我们在async函数中将标志设置为“true”,子函数将被重新创建,我们将看到创建的钩子消息,但它看起来像拐杖。我们还可以在组件中添加一个:键,并更新它以强制重新呈现,但它看起来也像拐杖。
任何关于这个主题的解释都是很好的。
1条答案
按热度按时间t40tm48m1#
Vue会尽可能地重用元素和组件。它也只会在一个刻度上重新呈现一次。“刻度”的长度不需要你太担心,在您的例子中,
this.exit()
和this.started = true
语句在同一个时钟周期内执行。存储在this.started
中的数据是最后一个价格点和当前价格点中的true
,因为它不会结束语句之间的价格点,因此组件不会发生任何变化。一般来说,你应该考虑Vue中的状态,而不是生命周期。或者换句话说:这个组件必须能够处理哪些不同的情况,以及如何在这些状态之间切换。与其确定在哪个时间点做什么,不如使用
:key="keyName"
实际上通常是一种拐杖,就像使用import { nextTick } from 'vue';
并使用它来获得一些状态发生的节奏一样,就像使用setTimeout
在当前tick之后执行一些代码一样。setTimeout
的讨厌之处还在于它可以在已经被破坏的组件上执行代码。尽管有时它对动画有帮助。根据我的经验,当人们尝试使用生命周期钩子时,他们宁愿在某个属性发生变化时发生一些事情。例如,当子组件上的一个属性
id
发生变化时,你希望从api加载数据来填充一些字段。要使其工作,请使用即时观察器:现在,它将在组件创建时调用观察器,然后在您传递一个不同的id时调用它。它还将帮助您优雅地处理这样的情况,即组件是使用您期望的一个prop中的
undefined
或null
值创建的。您不必抛出错误,而是在prop有效之前不呈现任何内容。