在 Vue v3 自定义元素组件中引用宿主元素
Referencing the host element in Vue v3 Custom Element components
好的,所以我知道这个问题的一些变体已经被问到,在 Vue 的各种版本和 APIs 中......但我一直无法弄清楚所以这是关于为什么我认为我的不同的上下文:
我正在尝试构建一些组件:
- 内部足够复杂以至于使用 Vue 而不是仅仅 native web components 构建是有帮助的,但是...
- 将 运行 在页面的 Vue 上下文之外(不在 Vue 应用程序中),因此被打包为 Web 组件/自定义元素 from Vue,并且...
- 实施将在
<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:
- 安装的 DOM 元素(影子根 上方 元素,即
<my-custom-element>
, 而不是 模板中的一些 ref()
), 和
- 要获取的组件的反应状态
value
...到目前为止,我主要使用 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;
});
好的,所以我知道这个问题的一些变体已经被问到,在 Vue 的各种版本和 APIs 中......但我一直无法弄清楚所以这是关于为什么我认为我的不同的上下文:
我正在尝试构建一些组件:
- 内部足够复杂以至于使用 Vue 而不是仅仅 native web components 构建是有帮助的,但是...
- 将 运行 在页面的 Vue 上下文之外(不在 Vue 应用程序中),因此被打包为 Web 组件/自定义元素 from Vue,并且...
- 实施将在
<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:
- 安装的 DOM 元素(影子根 上方 元素,即
<my-custom-element>
, 而不是 模板中的一些ref()
), 和 - 要获取的组件的反应状态
value
...到目前为止,我主要使用 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 答案
<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;
});