vuejs / core

🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
https://vuejs.org/
MIT License
47.8k stars 8.35k forks source link

two bugs about watch, ref, reactive #12333

Closed hfyZone closed 2 weeks ago

hfyZone commented 2 weeks ago

Vue version

3.5.12

Link to minimal reproduction

https://github.com/hfyZone/vue-playground

Steps to reproduce

  1. Run nom run dev.
  2. Click button "Test Jon" and button "Change to Setwie".
  3. After clicking button "Change to Setwie",ckick button "Test Jon" again.
  4. View the console.
  5. Click button "change Salary Lois" and button "Change to Brain".
  6. View the console.
  7. add {deep:true} to line 18th in the file Teacher.vue,Click button "change Salary Lois" and button "Change to Brain".
  8. View the console.

What is expected?

  1. A page is loaded.
  2. The console displays "Watch triggered!" and the info of an object after you click button "Test Jon";The score in the paragraph above the two buttons changes to a new score;Nothing happens when you click button "Change to Setwie".
  3. Nothing happens when you click button "Test Jon".
  4. Nothing is displayed after step 3.
  5. After you click "change Salary Lois" ,The salary in the paragraph above the two buttons changes;After you click button "Change to Brain", the name and the salary in the paragraph above the two buttons changes, the first button in this line changes to "changeSalary Brain".
  6. After you click "change Salary Lois" ,nothing is printed in the console;After you click button "Change to Brain", the info printed in the console.
  7. Same as step 5.
  8. After you click "change Salary Lois" and After you click button "Change to Brain", the info printed in the console.

What is actually happening?

Two bugs of vue@3.5.12

1 Vue fails to deal with a ref Object when watch triggered by the change of the reference of this ref Object

In Student.vue, if you click button 'Change To Stewie',then vue will fail to deal with this ref Object. The button 'Change To Stewie' just change the reference of the 'student' to a new Object {name:"Stewie", salary: 0}. 1

2 whether is deep traversal of the source forced if it is an object?

According to the document:

deep: force deep traversal of the source if it is an object, so that the callback fires on deep mutations.
https://vuejs.org/api/reactivity-core.html#watch

But in Teacher.vue, an object 'teacher' is passed to watch,
Only if {deep:true} is added, then changeSalary() will trigger watch;
Whether {deep:true} is added or not, changeToBrain() always triggers watch;
Firstly,when {deep:true} is not added, a change of a property or the entire Object leads to different performance of watch, the first one doesn`t trigger watch, the second one triggers watch;
Secondly,the document tells that 'force deep traversal of the source if it is an object',but you have to add {deep:true}, I dont know if this is a problem of the code or the document.
2 3

System Info

No response

Any additional comments?

Actually I am just learning vue and code accordingly to the document.I have no idea about whether the two usage above is best practice or not, Its just Contradictory with the document and nothing alike is in stack overflow, so I report the two problems.

sqal commented 2 weeks ago

Regarding 1) You can't replace reference to reactive state like that, it's explained here https://vuejs.org/guide/essentials/reactivity-fundamentals.html#limitations-of-reactive If you want to update reactive state with a new object, use Object.assign(student, newState). Other solution is using ref. The choice is yours which one you want to use

Regarding 2) it works as expected. The changeToBrain() function will always trigger the callback because you're assigning a new object. In changeSalary(), you only update the salary property, but the tracked object itself hasn't changed. That's why the watcher callback is not triggered unless you explicitly enable deep property tracking.

edison1105 commented 2 weeks ago

As @sqal said.