single-spa / single-spa-vue

a single-spa plugin for vue.js applications

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Question: Equivalent to domElementGetter in single-spa-react?

anarchist912 opened this issue · comments

Hello! First of all, what you are developing here is great!
We are currently testing whether we can build the next generation of our software on single-spa. We have Vue and React developers. So we'd like to integrate a Vue app into our main React app.

But as there a multiple microfrontends, we don't want to have to add a new mount point element for each, but instead solve it programmatically, like this is possible with single-spa-react:

I saw this in the https://gitlab.com/TheMcMurder/single-spa-portal-example and it works like a charm!

const reactLifecycles = singleSpaReact({
  React,
  ReactDOM,
  rootComponent,
  domElementGetter
})


function domElementGetter() {
  const id = "demo-vue"
  let el = document.getElementById(id)
  if (!el) {
    el = document.createElement('div')
    el.id = id
    document.getElementById("sspa-mount").appendChild(el)
  }
  return el
}

Is it possible with single-spa-vue? I tried some things, but did not get any results.. if I just mount on an existing DOM node, the Vue app is rendered properly.

Where should I hook in?

const vueLifecycles = singleSpaVue({
  Vue,
  appOptions: {
    render: h => h(() => SystemJS.import(`@module/demo3/app.js`)),
  },
})

export const bootstrap = vueLifecycles.bootstrap
export const mount = vueLifecycles.mount
export const unmount = vueLifecycles.unmount

Hi @anarchist912, yes it is possible to specify which dom element a single-spa-vue application goes into.

These docs hint at how to do it, but I think they could be more explicit. Here's an example of how:

const vueLifecycles = singleSpaVue({
  Vue,
  appOptions: {
    el: '#my-special-container',
    render: h => h(() => SystemJS.import(`@module/demo3/app.js`)),
  }
})

I have created single-spa/single-spa.js.org#147 to clarify this in the documentation.

I'm closing this issue because this is something single-spa-vue already supports. Feel free to comment further or reopen.

Hello Joel! Thanks for the quick reply!!

Sorry, I should have put my question more cleary. I know that it's possible to specify the id of the target dom element... The Docs were clear enough for me. 👍

But is it possible to create the element on the fly, if it does not exist yet?
Something like this? single-spa-react allows it..

let el = document.getElementById('renderTarget')
if (!el) {
    el = document.createElement('div')
    el.id = 'renderTarget'
    document.body.appendChild(el)
  }

Thank you very much in advance!

Ah I see now. single-spa-vue already creates the dom element and appends it to document.body, but you want to append it to a sspa-mount container instead.

You could do that with the following code:

const vueLifecycles = singleSpaVue({
  Vue,
  appOptions: {
    el: '#sspa-mount #app1',
    render: h => h(() => SystemJS.import(`@module/demo3/app.js`)),
  }
})

export const bootstrap = [
  () => {
    document
      .getElementById('sspa-mount')
      .appendChild(Object.assign(
        document.createElement('div'),
        {id: 'app1'}
      ))
  },
  lifecycles.bootstrap
]

export const mount = lifecycles.mount

export const unmount = lifecycles.unmount

Exporting an array of lifecycle functions will call them in order, so by the time that single-spa-vue tries to mount the application the dom element will have already been created.

Great! I just had to wrap that first function in the array into a promise, like so:

export const bootstrap = [
  () => new Promise((resolve, reject) => {
    document
      .getElementById('sspa-mount')
      .appendChild(Object.assign(
        document.createElement('div'),
        {id: 'app1'}
      ))
      resolve()
    })
  },
  lifecycles.bootstrap
]

Thanks again.

Ah yes - has to return a promise. Glad that is working for you