简体   繁体   English

如何在 Svelte 中注册全局组件?

[英]How can you register global components in Svelte?

Vue (at least Vue 2) allows developers to register components globally: Vue(至少 Vue 2)允许开发者全局注册组件:

import MyComponent from '@/components/MyComponent'
Vue.component('my-component-name', MyComponent)

Which then results into:然后导致:

  • You can write or use component libraries (like Vuetify, Quasar, Ant etc.) without needing to import their components explicitly.您可以编写或使用组件库(如 Vuetify、Quasar、Ant 等),而无需显式导入它们的组件。
  • You can easily override other global components by just calling Vue.component(...) when you want to extend or change their respective codebase without having direct access to the source code.当您想要扩展或更改它们各自的代码库而无需直接访问源代码时,只需调用 Vue.component(...) 即可轻松覆盖其他全局组件。

I read in another SO post that Svelte does not support global component registration.我在另一篇 SO 帖子中读到 Svelte 不支持全局组件注册。 However, I would still like to achieve the same results as given above.但是,我仍然希望获得与上面给出的相同的结果。

How would I approach this in Svelte?我将如何在 Svelte 中解决这个问题?

I rolled my own when I needed a similar function (it was very handy when adding some interactive components to a WordPress site).当我需要类似的 function 时,我自己推出了自己的产品(在向 WordPress 站点添加一些交互式组件时非常方便)。

Here's the code I used:这是我使用的代码:

/**
 * Turn a svelte element into a simple web component
 * The content inside the web component will be passed in as the `slot` attribute and it's of type: `Array<ChildNode>`
 * @param svelteElem 
 * @param elemName 
 */
export function elementify(svelteElem: any, elemName: string) {
  class newElem extends HTMLElement {

    svelteComp: any

    constructor() {
      super()
      //at this point the internal elements are not mounted yet, so we need to do it before the next `paint` cycle
      window.requestAnimationFrame(() => {
        let children: Record<string, Array<ChildNode>> = { '___': [] }
          Array.from(this.childNodes).forEach(elem => {
            let slotName = "___"
            if ((elem instanceof HTMLElement) && elem.getAttribute("slot")) slotName = elem.getAttribute("slot")
            if (!children[slotName]) children[slotName] = []
            children[slotName].push(elem)
            elem.remove()
          })
        let props: Record<string, any> = {
          slot: children,
          parent: this,
        }
    
        let attribs:Array<string>
        if (typeof(this.getAttributeNames)=="undefined"){
          attribs = Array.from(this.attributes).map(x=>x.name)
        } else {
          attribs = this.getAttributeNames()
        }
        attribs.forEach(attr => {
          props[attr] = this.getAttribute(attr)
        })
        let comp = this.svelteComp = new svelteElem({
          target: this,
          props
        })
        // transfer the properties and methods exposed by the component
        Object.getOwnPropertyNames(Object.getPrototypeOf(comp)).forEach(prop => {
          if (prop != "constructor") (this as any)[prop] = comp[prop]
        })
      })
    }

    // connectedCallback() {
    // }
    // disconnectedCallback(){
    // }
  }
  customElements.define(elemName, newElem);
}

To use it, you simply do this:要使用它,您只需执行以下操作:

import MyComponent from './components/MyComponent.svelte'
elementify(MyComponent,"my-component-name")

After that you can use the <my-component-name> tag and the Svelte component will be mounted inside that component.之后,您可以使用<my-component-name>标记,Svelte 组件将安装在该组件内。

My solution might have some flaws but it's been working for months in production.我的解决方案可能存在一些缺陷,但它已经在生产中运行了几个月。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM