动态实例化 Web 组件的方法之间的差异

Nil*_*ils 6 javascript web-component custom-element native-web-component

Web 组件(仅针对此问题的自主自定义元素)可以通过多种方式“实现”。

以下三个选项之间是否存在显着差异?

选项1:

const foo = document.createElement('foo-element');
document.body.appendChild(foo);
Run Code Online (Sandbox Code Playgroud)

选项 2:

const div = document.createElement('div');
div.innerHTML = '<foo-element></foo-element>'
const foo = div.firstElementChild;
document.body.appendChild(foo);
Run Code Online (Sandbox Code Playgroud)

选项 3:

const foo = new FooElement;
document.body.appendChild(foo);
Run Code Online (Sandbox Code Playgroud)

我基于 Karma/Mocha 堆栈编写了一些单元测试,并使用选项 3 创建了我的实例。

这是否足够,也就是说,我可以使用任一方法依赖具有相同状态/行为的组件,还是有必要使用所有不同的实例化选项重复我的所有测试?

document.createElement由于错误,我的 Web 组件之一无法使用实例化:

VM977:1 Uncaught DOMException: Failed to construct 'CustomElement':
The result must not have attributes
at <anonymous>:1:10
Run Code Online (Sandbox Code Playgroud)

可以毫无问题地实例化相同的组件这一事实new告诉我,在幕后,必须存在显着差异,尤其是new FooElement和之间document.createElement('foo-element')

我可以编写三个通用测试来测试所有三种实例化方式,当然,但这足够了吗?

或者我所有现有的测试都应该使用所有 3 个实例化选项运行?

或者换个方式问:

实例化后每个实例是否完全相同?(假设没有错误)

Boo*_*boo 4

foo-element如果您使用该方法注册为自定义 HTML 元素,这 3 种方法的差异就会显现出来CustomElementRegistry.define()。根据我的实验,第二种方法无法利用注册自定义元素提供的任何特殊处理。此外,第一种方法必须按如下方式完成:

document.createElement("p", { is: "foo-element" });
Run Code Online (Sandbox Code Playgroud)

作为示例,我已经定义了foo-element扩展标签。<p>

无论如何,一个例子可以更好地解释这一点。在下面的代码中,我定义了FooElement扩展<p>标签以使用文本“I am foo”自动初始化。

// Create a class for the element
class FooElement extends HTMLParagraphElement {
  constructor() {
    // Always call super first in constructor
    super();
    this.innerText = 'I am foo';
  }
}


// Define the new element (The CustomElementRegistry is available through the Window.customElements property):

customElements.define('foo-element', FooElement, { extends: 'p' });
Run Code Online (Sandbox Code Playgroud)

现在执行以下代码片段:

document.createElement("p", { is: "foo-element" });
Run Code Online (Sandbox Code Playgroud)
// Create a class for the element
class FooElement extends HTMLParagraphElement {
  constructor() {
    // Always call super first in constructor
    super();
    this.innerText = 'I am foo';
  }
}


// Define the new element (The CustomElementRegistry is available through the Window.customElements property):

customElements.define('foo-element', FooElement, { extends: 'p' });
Run Code Online (Sandbox Code Playgroud)

我们已经创建了所有三个元素,但只有渴求和第三个选项对实现所需的特殊处理有任何影响。如果您要检查文本,您会发现实际的封闭元素实际上是<p>标签。

就您而言DOMException,无论您是否注册了元素,您展示的前两种方法都不应导致异常。FooElement但是,如果不是合法节点(如上HTMLParagraphElement例中通过扩展创建的节点),则第三种方法将引发异常。因此,我需要更多关于您的例外情况的具体情况的信息。

更新

这里的类FooElement不是从标准元素继承的,并且会抛出异常:

window.onload = function() {
    class FooElement extends HTMLParagraphElement {
      constructor() {
        // Always call super first in constructor
        super();
        this.innerText = 'I am foo';
      }
    }

    customElements.define('foo-element', FooElement, { extends: 'p' });

    const div1 = document.createElement('div');
    document.body.appendChild(div1);
    const foo1 = document.createElement("p", { is: "foo-element" });
    div1.appendChild(foo1);

    const div2 = document.createElement('div');
    document.body.appendChild(div2);
    div2.innerHTML = '<foo-element></foo-element>';

    const div3 = document.createElement('div');
    document.body.appendChild(div3);
    const foo3 = new FooElement();
    div3.appendChild(foo3);

};
Run Code Online (Sandbox Code Playgroud)
<body>
</body>
Run Code Online (Sandbox Code Playgroud)