在 Vue v3 自定义元素组件中引用宿主元素

Referencing the host element in Vue v3 Custom Element components

好的,所以我知道这个问题的一些变体已经被问到,在 Vue 的各种版本和 APIs 中......但我一直无法弄清楚所以这是关于为什么我认为我的不同的上下文:

我正在尝试构建一些组件:

  1. 内部足够复杂以至于使用 Vue 而不是仅仅 native web components 构建是有帮助的,但是...
  2. 将 运行 在页面的 Vue 上下文之外(不在 Vue 应用程序中),因此被打包为 Web 组件/自定义元素 from Vue,并且...
  3. 实施将在 <form> 内使用的数据输入(同样,不在 Vue 应用程序中)。

其中一个挑战是 Vue Web 组件使用影子 DOM,并且表单不会自动遍历影子根来查找输​​入:所以让表单真正看到并提交组件的内部数据是不是自动的。

似乎有一些希望,如 this helpful blog post: A new ElementInternals API and element-internals-polyfill NPM 包中所详述,组件可以通过该包将数据指示到表单。实现“与表单相关的自定义元素”需要设置静态只读布尔值 属性(很简单),但也需要链接如下内容:

// (`this` == the custom HTMLElement itself)
const _internals = this.attachInternals();

_internals.setFormValue(value);

问题是,我真的很难弄清楚我可以在哪里挂钩以访问 both:

...到目前为止,我主要使用 Vue 的 composition and script setup APIs,无可否认,这让这变得更加困难:例如,onMounted 根本没有定义 this。但即使使用等效选项 API mounted: () => {} 我看到 this.$el 似乎是 template/shadow 根中的第一个元素,而不是拥有影子根的父自定义元素。

我也考虑过另一种方法——从创建的 CustomElement class 开始,然后尝试返回到有用的 Vue 数据和挂钩……但在这里也找不到方法:

import { defineCustomElement } from "vue";
import MyCustomComponent from "./components/MyCustomComponent.ce.vue"
const MyCustomElement = defineCustomElement(MyCustomComponent);
class MyCustomElementFormAssoc extends MyCustomElement {
  static get formAssociated() {
    return true;
  }

  constructor(initialProps?: Record<string, any> | undefined) {
    super(initialProps);
    const _internals = this.attachInternals();

    // But here the component isn't even mounted yet - this._instance doesn't
    // exist and presumably reactive state doesn't either, so can't do:
    //   _internals.setFormValue(someValueState);
  }
}
customElements.define("my-custom-element", MyCustomElementFormAssoc);

因此,一般来说,根据其他 Vue 3 答案 ,在我的例子中,我专门尝试访问 定义组件的自定义元素 -不是模板内的元素。呈现的 DOM 看起来像:

    <my-custom-element class="this-one-is">
      #shadow-root (open)
      <div class="custom-element-template-can-have-multiple-roots"></div>
      <div class="but-these-are-not-the-elements-im-looking-for"></div>
    </my-custom-element>

有人知道怎么做吗?

同意这是一种糟糕的代码味道,也是评估 Vue 是否真的适合一般用例的信号:Hacking around with hybrid Web Components that aren't quite native but aren't quite Vue either即使今天能用,也可能成为维护负担。

但必须 - 我目前的解决方法是通过 DOM 从模板中 中的一些参考元素(并不重要)追溯,像这样:

// (MyCustomComponent.ce.vue script setup)

import { ref } from "vue";

const someTemplateRef = ref();

onMounted(() => {
  const hostNode = root.value.getRootNode()?.host;
});