single-spa / single-spa-vue

a single-spa plugin for vue.js applications

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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".

appOptions.el = appOptions.el + " .single-spa-container";

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)

@iplus26, @raihle, the change is merged, if you can try it out and let me know how it goes, that would be great!

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.