Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Directives mounted called *after* beforeUpdate #12460

Open
jods4 opened this issue Nov 22, 2024 · 2 comments · May be fixed by #12482
Open

Directives mounted called *after* beforeUpdate #12460

jods4 opened this issue Nov 22, 2024 · 2 comments · May be fixed by #12482
Labels
🔨 p3-minor-bug Priority 3: this fixes a bug, but is an edge case that only affects very specific usage. scope: v-model

Comments

@jods4
Copy link
Contributor

jods4 commented Nov 22, 2024

Vue version

3.5.13

Link to minimal reproduction

https://play.vuejs.org/#eNp9VE1vEzEQ/SuWL03VZFMUeglJBFQ5wAGqtogDRmizO9m48dorfyxBq/3vjL1foSS52TPP4zfznl3RD0URlQ7onC5MonlhiQHrCiJimS2tWTHJ80JpSx7iDMhWq5wwGk39zp9jtAdUxOxiIdTvR9iSuoO2GCYTJY0lha+yPEKOpBPimkm89ZnnoJwdja7JchWQURkL5/H+ujG5u71F5GLaEEVquLGQFyK2sCKESUIW9wrZSJCWzLlZMurLMEqmiF5MezCTdEytQU5bnkUvRkkcQOULMJpgBS5Afy0sR86MzknI+Fxg/TnErHYw7uLJDpL9ifiLOfgYow8aDOgSqfQ5G+sMbJNeP32BA677ZK5SJxB9IfkIRgnnOTawj06mSPsIF9h+CupwmT2b9cGCNF1TnqhH1gEflPLDO9f6QHcWvQ3nmKxxit91XBSgz5sIj2KlwUgV0f8Z5N3gEJQcBUfIiNGNyxhFyZtMopy0oNustwKTWycTz5Uo+eSvRO8ExlilN8+V2l8h17POCb5JeRkW/yxxw2XhkNQEZw4Ce8GiaCf7pwDcGIh1sgv2QksrJ1KyAeypqkIbde2tH8pMz9T/ZqDvyyrfGBplh6HWxShA1QPqOng8FNk4a7Ht94ngyR6ptJibGxw1l0m0mDaI7tIjAm3ECIWPpJ1beCAD7PVTud9xkZ6SGNONOJBzi7NOYcslrHFjRj/8gBDD6E/UygNQ0jZy/h2/UiPcfIla9xOdYNYarnXo8HkdWfbo/wo3DaC+ZQ+5xLUrX078RFGK2FrdOD7MualbTjZcpkO2H3h7/ERnv0rQ/rViZ7PoLnozo/VfG1jmXA==

Steps to reproduce

Just open the repro.

What is expected?

The result should show [ ok ] should be "ok".

What is actually happening?

The result shows [ bug ] should be "ok".
Clicking on button "inc" will force a new render of the component, which will fix the page.

System Info

System:
    OS: Windows 10 10.0.19045
    CPU: (12) x64 Intel(R) Core(TM) i7-9850H CPU @ 2.60GHz
    Memory: 3.66 GB / 15.79 GB
  Binaries:
    Node: 21.7.0 - C:\Program Files\nodejs\node.EXE
    npm: 10.5.0 - C:\Program Files\nodejs\npm.CMD
  Browsers:
    Edge: Chromium (127.0.2651.98), ChromiumDev (129.0.2752.4)
  npmPackages:
    vue: ^3.5.13 => 3.5.13

Any additional comments?

This reproduction is extremely fragile and I had a hard time creating it.
The core of the issue is incorrect queued hooks execution order, and most changes you would do to the repro are going to fix it.
If you want to see the page working correctly from the get go, just remove the timeout in App.vue and directly set page.value = Page.

Here's what I found out when debugging this myself:

The input is driven by a v-model, so the value is not really "rendered" by script setup, but it's instead set on elements by vModelText core directive. This will explain why the interpolation {{ val }} is rendered with the correct value, whereas the <input> shows the initial value instead.

The reproduction is setup in such a way that vModelText hooks are called in incorrect order.
After event onSetup is processed by Wrapper, the component is rendered again, and vModelText.beforeUpdate is called with the new value. If you break at this point you'll see the repro displaying the correct values.

But then, it looks like the directive mounted hook has not been executed yet. It is enqueued by Vue core mountElement function here:

if (
(vnodeHook = props && props.onVnodeMounted) ||
needCallTransitionHooks ||
dirs
) {
queuePostRenderEffect(() => {
vnodeHook && invokeVNodeHook(vnodeHook, parentComponent, vnode)
needCallTransitionHooks && transition!.enter(el)
dirs && invokeDirectiveHook(vnode, null, parentComponent, 'mounted')
}, parentSuspense)
}

The issue seems to be that this call has captured the initial directive value and so restores the previous, incorrect value into the input element.

@edison1105 edison1105 added the 🔨 p3-minor-bug Priority 3: this fixes a bug, but is an edge case that only affects very specific usage. label Nov 27, 2024
@edison1105
Copy link
Member

edison1105 commented Nov 28, 2024

Additionally, the onMounted of the Wrapper.vue is also called after onBeforeUpdate. see Playground, Observe the console logs.

@linzhe141
Copy link
Contributor

A simpler minimal reproduction

We can see that the breakpoints in setTimeout, beforeUpdate, and mounted are triggered in sequence

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🔨 p3-minor-bug Priority 3: this fixes a bug, but is an edge case that only affects very specific usage. scope: v-model
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants