Home > Blockchain >  How to register and use props correctly in new vue custom element?
How to register and use props correctly in new vue custom element?

Time:11-05

I use new future defineCustomElement from vue version 3.2.

Code 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],
      })
    )

Code defineCustomElementWithStyles.js:

import { defineCustomElement as VueDefineCustomElement, h, createApp, getCurrentInstance } from 'vue'

const getNearestElementParent = (el) => {
  while (el?.nodeType !== 1 /* ELEMENT */) {
    el = el.parentElement
  }
  return el
}

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

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

      app.mixin({
        mounted() {
          const insertStyles = (styles) => {
            if (styles?.length) {
              this.__style = document.createElement('style')
              this.__style.innerText = styles.join().replace(/\n/g, '')
              getNearestElementParent(this.$el).prepend(this.__style)
            }
          }

          // load own styles
          insertStyles(this.$?.type.styles)

          // load styles of child components
          if (this.$options.components) {
            for (const comp of Object.values(this.$options.components)) {
              insertStyles(comp.styles)
            }
          }
        },
        unmounted() {
          this.__style?.remove()
        },
      })

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

Code App.ce.vue where I register and use prop:

<template>
  <div id="app">
    <p>{{ title }}</p>
    <nav>
      <router-link to="/">Home</router-link> |
      <router-link to="/about">About</router-link>
    </nav>
    <main>
      <router-view />
    </main>
  </div>
</template>
<script>
export default {
  name: 'App',
  props: {
    title: String,
  },
}
</script>

Code index.html where I set value fro prop title:

<app-root title="hello"></app-root>

Get error with message:

Uncaught TypeError: Cannot read properties of undefined (reading 'title')

Full demo project here

CodePudding user response:

Since the App component is being wrapped in defineCustomElementWithStyle, the wrapper's props should be passed to App within setup().

  1. Copy the component's props into the wrapper's props:

  2. Move the wrapper's render prop into a return from setup().

  3. Pass setup()'s props argument to the rendering of App (h() receives props as its 2nd argument).

export const defineCustomElement = (component, { plugins = [] }) =>
  VueDefineCustomElement({
    props: component.props, 1️⃣
    // render: () => h(component), 2️⃣
    setup(props 3️⃣) {
      ⋮
      return () => h(component, props 3️⃣)
    }
  })

demo

  • Related