admin管理员组

文章数量:1195167

I have project with nested custom elements. Now I need to vuex & vue router. How I can use this packages from root custom element and then use in all child custom elements?

Currently I tried only use vuex inside each component like this:

<script>
import store from './store';

export default {
  setup() {
    const state = store.state;

    return { state };
  },
};
</script>

Here is demo project with nested custom elements

Here is my main.js file code:

import { defineCustomElement } from "./defineCustomElementWithStyles";
import App from "./App.ce.vue";

customElements.define("app-root", defineCustomElement(App));

I have project with nested custom elements. Now I need to vuex & vue router. How I can use this packages from root custom element and then use in all child custom elements?

Currently I tried only use vuex inside each component like this:

<script>
import store from './store';

export default {
  setup() {
    const state = store.state;

    return { state };
  },
};
</script>

Here is demo project with nested custom elements

Here is my main.js file code:

import { defineCustomElement } from "./defineCustomElementWithStyles";
import App from "./App.ce.vue";

customElements.define("app-root", defineCustomElement(App));
Share Improve this question edited Nov 2, 2021 at 9:36 Andreas Hunter asked Nov 2, 2021 at 9:30 Andreas HunterAndreas Hunter 5,00418 gold badges75 silver badges142 bronze badges
Add a comment  | 

3 Answers 3

Reset to default 24

Vue plugins require the app instance, which is not defined for components created from defineCustomElement(). As a workaround, you can install the plugins on a temporary app instance, and then copy the resulting context over to the actual app, as seen in this utility (which we'll use later):

// defineCustomElementWithStyles.js
import { defineCustomElement as VueDefineCustomElement, h, createApp, getCurrentInstance } from 'vue'

export const defineCustomElement = (component, { plugins = [] } = {}) =>
  VueDefineCustomElement({
    render: () => h(component),
    setup() {
      const app = createApp()

      // install plugins
      plugins.forEach(app.use)

      const inst = getCurrentInstance()
      Object.assign(inst.appContext, app._context)
      Object.assign(inst.provides, app._context.provides)
    },
  })

Use the defineCustomElement() above instead of the one from vue:

// main.js
import { defineCustomElement } from './defineCustomElementWithStyles'
import App from './App.ce.vue'
import store from './store'
import router from './router'

customElements.define(
  'app-root',
  defineCustomElement(App, {
    plugins: [store, router],
  })
)

demo

Just a little fix for tony19's solution:

export const defineCustomElementWrapped = (component, { plugins = [] } = {}) =>
  defineCustomElement({
    styles: component.styles, // <- this
    render: () => h(component),
    setup() {
      const app = createApp()

      // install plugins
      plugins.forEach(app.use)

      const inst = getCurrentInstance()
      Object.assign(inst.appContext, app._context)
      Object.assign(inst.provides, app._context.provides)
    },
  })

and then

customElements.define(
  'app-root',
  defineCustomElementWrapped(App, {
    plugins: [store, router],
  })
)

This will also include styles exported by components

(also I don't like shadowing original function names if you are changing their behaviors, so I renamed it)

Might be also required:

    styles: component.styles.map((style) => {
      return style
        .replace(":root", ":host") // <- rename :root values to :host
        .replace("body {", "#my-app {"); // <- in case of frameworks like bootstrap that styles directly body
    }),

Just a small adjustments to the great answers: when you need props, emits or styles, it might be easier to destructure the component instead of wrapping it:

const defineCustomElement = (component, { plugins = [] } = {}) =>
  VueDefineCustomElement({
    ...component, // <---- use all props from the original component (except setup)
    setup(...args) {
      const app = createApp({})
      plugins.forEach(app.use)
      const instance = getCurrentInstance()
      Object.assign(instance.appContext, app._context)
      Object.assign(instance.provides, app._context.provides)

      return component.setup?.(...args) // <---- run initial setup if exists
    },
  })

本文标签: javascriptHow to use vue router and vuex inside custom element from rootStack Overflow