vuejs / vue

This is the repo for Vue 2. For Vue 3, go to https://github.com/vuejs/core
http://v2.vuejs.org
MIT License
207.95k stars 33.67k forks source link

watch for keep-alive component has error, for basic type prop is failed, for object type is successed! #12700

Closed yixiu025 closed 2 years ago

yixiu025 commented 2 years ago

Version

2.6.14

Reproduction link

codesandbox.io

Steps to reproduce

  1. use v-show and without keep-alive watch basic type prop or object prop all successes!
  2. use v-if with keep-alive watch basic type prop failed and watch object prop is successes! notice: use dev-tools plugin watch component boolean prop, no matter how many times you switch tabs, boolean prop always is true, when i click buton update object value, object prop watch handler method has been called!

What is expected?

if object type prop watch success then basic type prop watch should be success or basic type prop watch failed then object type prop watch should be failed

What is actually happening?

if object type prop watch success then basic type prop watch should be success or basic type prop watch failed then object type prop watch should be failed


My English is bad, sorry!

MinatoHikari commented 2 years ago

you need to change to card once in your reproduction. when using v-if the card component won't mount at the beginning.

yixiu025 commented 2 years ago

you need to change to card once in your reproduction. when using v-if the card component won't mount at the beginning.

@MinatoHikari Thank you for your reply! I'm trying to append that code in mounted():

mounted() {
  this.showType = SHOW_TYPE_CARD;

  this.$nextTick(() => {
    this.showType = SHOW_TYPE_LIST;
  });
}

But it's invalid when i toogle to card or list. vue-devtools plugin show that: image when i toogle to card or list(change boolean prop): image when i toogle query condition(change object prop): image Did I misunderstand what you meant?

I think if the problem is caused by use v-if directive not mounting the dom node, then the watcher for the object should also be invalid.Or they should all succeed.

MinatoHikari commented 2 years ago

I think if the problem is caused by use v-if directive not mounting the dom node, then the watcher for the object should also be invalid.Or they should all succeed.

wut? In your reproduction: add

mounted() {
    this.toogleShowType(SHOW_TYPE_CARD)
  },

then click the green button twice. you can see the console

list condition change:  {enabled: false}
card condition change:  {enabled: false}
list condition change:  {enabled: true}
card condition change:  {enabled: true}
yixiu025 commented 2 years ago

I think if the problem is caused by use v-if directive not mounting the dom node, then the watcher for the object should also be invalid.Or they should all succeed.

wut? In your reproduction: add

mounted() {
    this.toogleShowType(SHOW_TYPE_CARD)
  },

then click the green button twice. you can see the console

list condition change:  {enabled: false}
card condition change:  {enabled: false}
list condition change:  {enabled: true}
card condition change:  {enabled: true}

Well, whether in my reproduction add that code, Clicking the green button will output the above. But Clicking the blue button back and forth will not trigger the following message like this:

card show value: true
list show value: false
card show value: false
list show value: true

I added listeners to both the show and condition props. I want to listen to the change of the show property when the blue button is clicked and Listen to the condition property change when the blue button is clicked.

MinatoHikari commented 2 years ago

Well, whether in my reproduction add that code, Clicking the green button will output the above. But Clicking the blue button back and forth will not trigger the following message like this:

yixiu025 commented 2 years ago
card show value: true
list show value: false
card show value: false
list show value: true

This is the result I expect, but it doesn't appear. I don't understand why the listening for primitive types and object types is inconsistent when using the keep-alive component.

thanks for your reply.

MinatoHikari commented 2 years ago
card show value: true
list show value: false
card show value: false
list show value: true

This is the result I expect, but it doesn't appear. I don't understand why the listening for primitive types and object types is inconsistent when using the keep-alive component.

thanks for your reply.

I get it. Props will not be changed when component be deactivated. PropsData which child need in father component will only be passed to active component. Check the keep-alive component's source. https://codesandbox.io/s/vue-keep-alive-watch-test-forked-kvpdp9?file=/src/components/List.vue:1901-1912

yixiu025 commented 2 years ago

I get it. Props will not be changed when component be deactivated. PropsData which child need in father component will only be passed to active component. Check the keep-alive component's source. https://codesandbox.io/s/vue-keep-alive-watch-test-forked-kvpdp9?file=/src/components/List.vue:1901-1912

Aha, I finally solved this problem with activated and inactivated hook functions.

The following code you added causes the property to be modified when the green button is clicked to be non-responsive, which ultimately affects the condition prop listening: image

If you comment out the code in the above box, you will get the following result when you click the green button(Please click the blue button to switch the card first): image If PropsData which child need in father component will only be passed to active component. I should get the result like this(Suppose the currently activated component is List):

list condition change: {enabled: false}
MinatoHikari commented 2 years ago

I use this.condition = {}; to tell you that object's behavior is same as boolean or string as a prop in keep-alive component.

此处是修改引用试图触发子组件的watch,你把keep-alive去掉就能看到每次赋值{}都会触发watch

You should know that enabled is property of condition first, not a prop of component.

你点击绿色的按钮修改的是引用类型对象的属性,而不是在修改传入的prop condition的引用

yixiu025 commented 2 years ago

I use this.condition = {}; to tell you that object's behavior is same as boolean or string as a prop in keep-alive component.

此处是修改引用试图触发子组件的watch,你把keep-alive去掉就能看到每次赋值{}都会触发watch

You should know that enabled is property of condition first, not a prop of component.

你点击绿色的按钮修改的是引用类型对象的属性,而不是在修改传入的prop condition的引用

Just dealt with something else, sorry about that! 刚刚处理了点别的事情,非常抱歉!

Referring to your answer, for a listener with deep: true added, will handler() be triggered under any circumstances as long as the property value of the object is modified (even if the component is wrapped in keep-alive and it is not Activated)? 参考您的答复,对于添加了deep: true的侦听器,只要修改了对象的属性值,无论在何种情况下都会触发handler()吗(即使组件被包裹在keep-alive中并且它并未被激活)?

I saw some instructions for deep on the official website: 我在官网看到了deep的一些说明:

image But my understanding is that deep belongs to watch, then watch, as the outermost controller, should block the execution of handler() when listening is found in components that are not activated in keep-alive. 但我的理解是deep隶属于watch,那么watch作为最外层的控制方,在keep-alive中未被激活的组件中发现监听时应该阻断handler()函数的执行。

The official website does not give a more detailed description of deep, please help answer my doubts, thank you. 官网并没有对deep作出比较详细的说明,还请您帮忙解答下我的疑惑,谢谢。

MinatoHikari commented 2 years ago

感觉已经说的挺明确了啊…… watch is always in working,but deactivited component can't get propsData passed from father,so that object prop in child component never be changed in deactivited, and when you call this.condition = {} nothing happen. But in activited hook the new object can be passed by father, so this.condition = {} can be detected. watch is always in working,object doesn't be changed, then property in this object with deep:true option can be detected.

yixiu025 commented 2 years ago

ok i see, thank you very much~ 好的,我明白了,非常感谢您~

MinatoHikari commented 2 years ago

ok i see, thank you very much~ 好的,我明白了,非常感谢您~

没点蓝色按钮触发object引用链断裂的时候点绿色按钮,因为deep watch的原因,未激活的组件也能侦听到变化 而当点击一次蓝色按钮后引用链断开了,可尝试使用$set触发激活的组件的deep watch,但是未激活因为没有接收到父组件传来的新的引用对象,condition的引用并未发生变化,所以此时修改父组件condition内部的属性应该也是无法触发未激活的deep watch的

你可以自行实验一下

yixiu025 commented 2 years ago

没点蓝色按钮触发object引用链断裂的时候点绿色按钮,因为deep watch的原因,未激活的组件也能侦听到变化 而当点击一次蓝色按钮后引用链断开了,可尝试使用$set触发激活的组件的deep watch,但是未激活因为没有接收到父组件传来的新的引用对象,condition的引用并未发生变化,所以此时修改父组件condition内部的属性应该也是无法触发未激活的deep watch的

你可以自行实验一下

好的,我抽空实验下,谢谢。

yixiu025 commented 2 years ago

没点蓝色按钮触发object引用链断裂的时候点绿色按钮,因为deep watch的原因,未激活的组件也能侦听到变化 而当点击一次蓝色按钮后引用链断开了,可尝试使用$set触发激活的组件的deep watch,但是未激活因为没有接收到父组件传来的新的引用对象,condition的引用并未发生变化,所以此时修改父组件condition内部的属性应该也是无法触发未激活的deep watch的

你可以自行实验一下

我按照您给的思路测试了下,最终的表现形式确实和您所说的一致。 然后,我在子组件里面添加了一个String类型的prop,并在切换当前显示的子组件时修改了它的值,我发现当前激活的组件监听到了它的变化,回想一下针对于Boolean类型的prop不是监听失败了,而是它的值在当前激活组件中一直是true,所以不会触发对应的watcher,也就不会按照我的预期输出相应的调试信息了。 那么我最终的结论是否可以总结为: keep-alive和deep watch同时使用时,keep-alive包裹的组件切换时,对于deep watcher需要强制修改其引用,以使其断开未激活组件的deep watcher呢?

MinatoHikari commented 2 years ago

我按照您给的思路测试了下,最终的表现形式确实和您所说的一致。 然后,我在子组件里面添加了一个String类型的prop,并在切换当前显示的子组件时修改了它的值,我发现当前激活的组件监听到了它的变化,回想一下针对于Boolean类型的prop不是监听失败了,而是它的值在当前激活组件中一直是true,所以不会触发对应的watcher,也就不会按照我的预期输出相应的调试信息了。 那么我最终的结论是否可以总结为: keep-alive和deep watch同时使用时,keep-alive包裹的组件切换时,对于deep watcher需要强制修改其引用,以使其断开未激活组件的deep watcher呢?

你可以用$watch这个api,它返回一个unwatch,调用以后停止监听,在activited和deactivited两个hooks中watch和unwatch 这样比较科学易理解,最好不要利用引用对象特性来实现,对维护非常不友好

yixiu025 commented 2 years ago

你可以用$watch这个api,它返回一个unwatch,调用以后停止监听,在activited和deactivited两个hooks中watch和unwatch 这样比较科学易理解,最好不要利用引用对象特性来实现,对维护非常不友好

好的,谢谢您。