Skip to content

onServerPrefetch with useId cause hydration issue if treeshaken client-side #12591

Open
@huang-julien

Description

@huang-julien
Contributor

Vue version

3.5.13

Link to minimal reproduction

https://stackblitz.com/edit/vue-ssr-example-bsqzmafk?file=app.js,client.js,server.js

Steps to reproduce

open your console and see there's a hydration mismatch.

Comp.vue's useId is not retturning the same value server side and client side

What is expected?

no hydration issue due to useId

What is actually happening?

The cause of this issue is that we run onServerPrefetch only server-side.

This happen in Nuxt because we treeshake onServerPrefetch in client-bundle and we also wrap onServerPrefetch with import.meta.server flag in useAsyncData composable.

In runtime, this results into the component being marked as async boudary only server side with markAsyncBoundary but not client side --> resulting to a hydration issue because useId returns different strings.

System Info

No response

Any additional comments?

Nuxt issue nuxt/nuxt#30289

Activity

added
🔨 p3-minor-bugPriority 3: this fixes a bug, but is an edge case that only affects very specific usage.
on Dec 23, 2024
edison1105

edison1105 commented on Dec 23, 2024

@edison1105
Member

When rendering on the CSR, it checks if the component contains onServerPrefetch hook at

if ((isAsyncSetup || instance.sp) && !isAsyncWrapper(instance)) {
// async setup / serverPrefetch, mark as async boundary for useId()
markAsyncBoundary(instance)
}
If it does, it calls markAsynvBoundary to make it consistent with SSR behavior.

added and removed
🔨 p3-minor-bugPriority 3: this fixes a bug, but is an edge case that only affects very specific usage.
on Dec 23, 2024
huang-julien

huang-julien commented on Dec 23, 2024

@huang-julien
ContributorAuthor

In Nuxt side, we could either treeshake only the callback of onServerPrefetch in client side bundle or add instance.sp = instance.sp || [].

But i'm not really in favour of the second one tho.

Or maybe can we leave it to vue: we remove the treeshaking of onServerPrefetch and maybe can the vue compiler do the treeshaking for client-side bundle ? WDYT ?

cc @danielroe

cernymatej

cernymatej commented on Dec 23, 2024

@cernymatej

imho, it would be nice to tree-shake it on the Vue side

danielroe

danielroe commented on Dec 23, 2024

@danielroe
Member

I'm happy to remove treeshaking of onServerPrefetch, more generally, and leave it up to the vue compiler - but it's not quite that straightforward. In this case, the call to onServerPrefetch is in an entire separate if/then fork (because data fetching on the server and in the hydration process in the browser run differently).

  // Server side
  if (import.meta.server && fetchOnServer && options.immediate) {
    const promise = initialFetch()
    if (getCurrentInstance()) {
      onServerPrefetch(() => promise)
    } else {
      nuxtApp.hook('app:created', async () => { await promise })
    }
  }

https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/asyncData.ts#L338-L347

In other words, we'd have to artificially add an 'onServerPrefetch' which awaits an empty promise in order to 'match'. More than happy to do that, but is there another way?

huang-julien

huang-julien commented on Dec 23, 2024

@huang-julien
ContributorAuthor

Yes, useAsyncData is a too specific case to be handled by vue-compiler. In the meantime, we can try the second option using an empty instance.sp = instance.sp || []
Or fully remove the treeshaking of onServerPrefetch and we try treeshaking the callback in Nuxt side ?

edison1105

edison1105 commented on Dec 24, 2024

@edison1105
Member

Vue compiler cannot find all onServerPrefetch calls during compilation. Apart from the scenario mentioned by @danielroe, users may also call an externally imported function, which internally calls onServerPrefetch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @edison1105@danielroe@huang-julien@cernymatej

        Issue actions

          onServerPrefetch with `useId` cause hydration issue if treeshaken client-side · Issue #12591 · vuejs/core