Subscribe on changes!

template refs on suspended components expose component instance early, before it has been resolved or properties been exposed.

avatar
Dec 14th 2022

Vue version

3.2.45

Link to minimal reproduction

https://stackblitz.com/edit/vitejs-vite-a4rmmb?file=src%2FApp.vue,src%2Fcomponents%2FHelloWorld.vue,package.json,package-lock.json&terminal=dev

Steps to reproduce

Run sample It uses watch on a component ref to know when there is something to act on. When the component ref is set we try to call a method on that child component. However, in 3.2.45 something changed and if the child component is async we can't see the method.

3.2.44 version (working as intended)

3.2.45 version (example breaks because method isn't exposed)

What is expected?

Receiving the child component back in the component ref and being able to see and call exposed methods on it.

What is actually happening?

The object returned doesn't have the exposed method available.

System Info

No response

Any additional comments?

We have a microfrontend that in some cases need to be able to call methods on "top-level" components from outside Vue.

Might be a better way to do it than this example (would be happy to be pointed in a good direction), however it did work prior to 3.2.45.

avatar
Dec 15th 2022

as workaround

// Move this to the front of top level await
defineExpose({
  callMe,
});
  
// Make async
await new Promise((resolve) => setTimeout(resolve, 1));
avatar
Dec 15th 2022

duplicate of https://github.com/vuejs/core/issues/4930 In fact, version 3.2.45 will also have problems with production.

avatar
Dec 15th 2022

as workaround

// Move this to the front of top level await
defineExpose({
  callMe,
});
  
// Make async
await new Promise((resolve) => setTimeout(resolve, 1));

That works, but would require all teams to make changes and remember the order. Is there no way to work around it from outside the component? Or will there be a fix and we just have to postpone upgrading?

avatar
Dec 15th 2022

@SimmeNilsson this PR https://github.com/vuejs/core/pull/4951 will fix it.

avatar
Dec 15th 2022

@edison1105 #4951 was too outdated, author just closed it. I'll reopen this issue here until we have a fresh fix. Also, since it works in 3.2.44, it must be something that changed in the meantime, right?

avatar
Dec 15th 2022

it must be something that changed in the meantime, right?

@LinusBorg Yes, and is not working all the time in reproduction mode.

avatar
Dec 15th 2022

Ah, got it. in 3.2.45, we closed a few loopholes in the difference between development and production behaviour of compiled script-setup components.

One of these "loopholes" made the code in this example work in development with 3.2.44. And as you said, it never worked in production in either version.

That's because the ref is being set before instance.exposed is being set. The ref is set when the async component is being mounted by Suspense into an inactive side branch, where it stays until it has resolved. at that point, exposed() hasn't been called yet.

Now I see two possible ways around that:

  1. The ref should be set later: We set the ref only after the async component has resolved.
  2. The ref should still be set as early as it is now, but the expose proxy should be properly provided, independent of when exposed()is being called.

But 2. isn't really a proper solution: We would expose the correct proxy now, but the callMe method still would not be exposed on that proxy until right before the component resolved, because only then does the example call expose().

avatar
Feb 6th 2023

Any idea if/when a fix will be made? :)

avatar
Dec 18th 2023

i am dealing with the same issue. Last comments were late 2022. Is there something new?