Vue instance is not mounted at the exact element `el`
iplus26 opened this issue · comments
Hi, joel, it's been a month since I submitted a (my only) pr here 🤝
So the story is, I maintain a "micro-frontend website" in our team and one of my client's trying to migrate his "ssr vue application" into our single-spa arch.
After he reported the issue that the vue instance is not mounted at #app
, I dig a little and found that: whether appOptions.el
is provided, single-spa-vue
append " .single-spa-container"
selector after the original selector. In this case, the el
selector finally passed to new Vue
is "#app .single-spa-container"
.
single-spa-vue/src/single-spa-vue.js
Line 100 in 6ead446
I suppose this line is brought in pr #34 to solve issue #33 .
I'm using qiankun
with single-spa-* plugins. As you know, instead of JS entry, qiankun
supports "html entry", therefore, ssr support may be a not only possible but may be important feature to us. Under this situation, I'm wondering there's any approach that we can leave el
selector alone, and let Vue instance mount on the exact element? Maybe something like:
if (!opts.exactEl) {
appOptions.el = appOptions.el + " .single-spa-container";
}
I found qiankun
choose not to use single-spa-vue
in its official examples. 👀
Therefore, if it's a hard decision for you, joel, or it's the opposite to the original design, feel free to close this issue and I'm also happy to maintain a fork of single-spa-vue for our ssr users to use.
Hi @iplus26, this behavior is intentional. We did it so that the behavior of single-spa-vue is consistent with the behavior of all the other frameworks. For all of the frameworks that we support, we append to props.domElement instead of replacing it. The Vue library replaces the element that it is mounted to, so we wrapped it in a container so that we could simulate appending instead of replacing.
I see. Thank you for your reply and insights.
I'm closing this issue for now.
As far as I understand, this interferes with server-side rendering (or rather, client-side hydration) as Vue expects the client-side app to mount on the root element rendered by the server-side Vue app. I don't love it, but as an app developer you can work around this by simply adding a single-spa-container
class to your root element, which will match the selector added by single-spa-vue.
As far as I understand, this interferes with server-side rendering (or rather, client-side hydration) as Vue expects the client-side app to mount on the root element rendered by the server-side Vue app. I don't love it, but as an app developer you can work around this by simply adding a
single-spa-container
class to your root element, which will match the selector added by single-spa-vue.
Yes, that's another approach. However, as you mentioned, it's a bit tricky so I decide to "betray" single-spa-vue and implement mount
& unmount
myself.
Thanks for the clarifications here about how this relates to SSR. I'm open to making this a configurable option. Would either of you care to create a pull request doing so? Maybe like this:
const lifecycles = singleSpaVue({
// all the normal options
...,
// new option
replaceMode: true
})
@joeldenning Maybe I can spare some time this weekend.
@joeldenning is #73 enough? Or I'm being way too simplistic?
Edit:
Just realized that this is exactly what @iplus26 commented when the issue was opened: #64 (comment)
It's released in https://github.com/single-spa/single-spa-vue/releases/tag/v2.3.0 and documented in single-spa/single-spa.js.org#443. You can see the documentation for replaceMode
at https://single-spa.js.org/docs/ecosystem-vue#options
Thanks @MarcoBomfim for your help with this one.