In a special case, the attribute binding will fail.
Version
3.2.26
Reproduction link
Steps to reproduce
As a comparison, enter the following two sets of characters in the input box.
- Enter '1', '2', '3', '4' in order. They are completely different characters.
- Enter 'a', 'a', 'a', 'a' in order. They are completely same characters.
What is expected?
In the second case, there should always be a single "a" in the input box.
What is actually happening?
In the second case, Vue does not do anything. The input box behaves just like its default behavior.
Here is my speculation:
Normally, the "value" of the input box is the same as the "text". Therefore, for efficiency, when the value assigned to "text" is the same as the previous one, it is not re-rendered.
However, in the callback of the "input" event, the "value" is the new content entered by the user, and the "text" is the old content that has not been changed. If you try to revert the input box to the old content (by changing the "text"), it will be ignored by Vue.
This behavior is also present on other forms.
(This issue was modified from machine translation. If something is not clear, please feel free to ask.)
I believe that this is the same case as https://github.com/vuejs/vue/issues/7506#issuecomment-372139382 and it always worked like that
When you type "a"
, component re-renders because previous value of text
was "3"
, but when you type the same letter for the second time, component won't update because value has not changed. You can fix this by setting a new value of the input field in your @input handler.
I think the reason of this bug is the hasChanged
function in here: https://github.com/vuejs/vue-next/blob/master/packages/reactivity/src/baseHandlers.ts#L170
In this case, the text you input is same as you input before, so it will not trigger effect or update component.
If we remove the hasChanged
function, this bug will be fixed. But this comes with additional performance overhead.
Is there a better way to fix it?
After testing, this method fails in the following two cases.
triggerRef
only works on refs, so it's expected that it doesn't work onthis.text
.- For case (2), a component won't update if it receives the same props passed to it. In this case you'll have to manually update the input in the child component:
I've come up with an argument for this behavior of Vue. This might be an okay explanation:
Vue always assumes that the bound data is the same as the real value of the form. The advantage of this is that when the bound data is assigned by its old value, Vue can ignore the assignment to optimize performance.
However, this assumption is broken in the event that the value of the form is modified by user. At this point, the real value of the component is the value just entered by the user, and the bound data has not yet been updated. In this case, if the bound data is modified with the old value, Vue will incorrectly perform the optimization.
Therefore, to ensure that this assumption always holds. v-modle should always be used to make the data consistent with the true value.