【问题标题】:How to define all custom elements before any constructors are called?如何在调用任何构造函数之前定义所有自定义元素?
【发布时间】:2019-08-28 20:52:53
【问题描述】:

我有两个自定义元素:OneTwoOne 有一个功能。 Two 有一个 One 的孩子,并尝试在其构造函数中调用该函数。它说如果我在One 之前在Two 上调用customElements.define(),则该函数不存在。但是,如果我在Two 之前定义One,它就可以正常工作。

在我的实际项目中,我无法控制它们的定义顺序,默认情况下它们的定义顺序错误。

我尝试调用connectedCallback()中的函数,但也失败了。

具体什么时候调用构造函数?

有什么方法可以确保在调用任何构造函数之前都定义了它们?

class One extends HTMLElement {
  constructor() {
    super()
    console.log('one constructor')
  }
  
  myFunc() {
    console.log('it worked!')
  }
}

class Two extends HTMLElement {
  constructor() {
    super()
    console.log('two constructor')
    
    this.innerHTML = '<my-one></my-one>'
    this.myOne = document.querySelector('my-one')
    
    // this part fails
    this.myOne.myFunc()
  }
  
  connectedCallback() {
    // this also fails
    this.myOne.myFunc()
  }
}

// this works
// customElements.define("my-one", One)
// customElements.define("my-two", Two)

// this breaks
customElements.define("my-two", Two)
customElements.define("my-one", One)
&lt;my-two&gt;&lt;/my-two&gt;

【问题讨论】:

  • 元素二依赖于元素一存在,所以你有点需要等到该元素被定义。这样做的现代解决方案是在其自己的模块中定义每个自定义元素,并确保以 import One from "./one.js"; 启动两个模块,以便浏览器可以强制解析顺序,即使网络加载顺序完全不同。
  • 至于何时调用构造函数:在注册自定义元素的element upgrade part期间调用构造函数时不带参数,所以没有“声明”自定义元素不构造一个:part定义过程的核心是验证元素 可以 被构建,实际上是构建一个。
  • (可以在 html.spec.whatwg.org/multipage/… 上找到更多好的建议:例如,在 connectedCallback 之前不要使用该 innerHTML 和选择器代码。不要在构造函数中这样做)

标签: javascript html custom-element


【解决方案1】:

这一切都与 Web 组件的生命周期有关,以及当标签从 HTMLUnknownElement 升级到您的实际组件时

一个组件分两步定义。

1) 类的定义 2) 拨打customElements.define

是的,这两个可以在一起:

customElements.define('dog-food', class extends HTMLElement{});

但是类定义仍然发生在customElements.define 被调用之前。

只有在发生两件事时,元素才会升级为自定义元素:

1) 自定义元素必须使用customElements.define 和 2) 自定义元素必须要么 1) 使用document.createElementnew MyElement 实例化 2) 被添加到 DOM 树中

此示例将元素放入 DOM,但 1 秒内未定义。

我在定义之前显示构造函数,然后在定义之后再次显示。

class One extends HTMLElement {
  constructor() {
    super();
    console.log('one constructor');
  }

  connectedCallback() {
    this.innerHTML = "I have upgraded";
  }
}

let el = document.querySelector('my-one');

setTimeout(() => {
  customElements.define("my-one", One);
  console.log('after: ', el.constructor.toString().substr(0,30));
}, 1000);

console.log('before: ', el.constructor);
  
&lt;my-one&gt;I am just a simple element&lt;/my-one&gt;

在您的代码中,您使用innerHTML 来“加载”&lt;my-one&gt;。但是由于 &lt;my-two&gt; 在调用构造函数时可能还没有“真正”在 DOM 中,所以 innerHTML 不会在 DOM 中,因此,&lt;my-one&gt; 还不会升级。

您可以做的一件事是等待&lt;my-two&gt; 组件真正放入DOM,方法是等待更改connectedCallback 函数中的innerHTML

class One extends HTMLElement {
  constructor() {
    super();
    console.log('one constructor');
  }
  
  myFunc() {
    console.log('it worked!');
  }
}

class Two extends HTMLElement {
  constructor() {
    super();
    console.log('two constructor');
  }
  
  connectedCallback() {
    this.innerHTML = '<my-one></my-one>';
    setTimeout(() => {
      this.myOne = document.querySelector('my-one');
      this.myOne.myFunc();
    });
  }
}

customElements.define("my-two", Two)
customElements.define("my-one", One)
&lt;my-two&gt;&lt;/my-two&gt;

您会注意到,我仍然必须将对 &lt;my-one&gt; 中的函数的调用置于 setTimeout 调用中。这是因为 &lt;my-one&gt; 元素只有在您的 connectedCallback 函数存在之后才能升级。升级需要一个运行的机会,它不会在你的函数中间运行。

另一种方法是直接调用&lt;my-one&gt; 的构造函数:

class One extends HTMLElement {
  constructor() {
    super();
    console.log('one constructor');
  }

  connectedCallback() {
    this.innerHTML = "one";
  }

  myFunc() {
    console.log('it worked!');
  }
}

class Two extends HTMLElement {
  constructor() {
    super();
    console.log('two constructor');
  }
  
  connectedCallback() {
    customElements.whenDefined('my-one').then(() => {
      this.myOne = document.createElement('my-one');
      this.append(this.myOne);
      this.myOne.myFunc();
    });
  }
}

customElements.define("my-two", Two);
customElements.define("my-one", One);
&lt;my-two&gt;&lt;/my-two&gt;

在这里您会注意到我必须添加对customElements.whenDefined 的调用。这将等到&lt;my-one&gt; 被实际定义,然后再尝试实例化它。一旦它被定义,你就可以创建它并立即调用成员函数。

最后一件事。对于在 Web 组件的构造函数中应该做什么和不应该做什么,有一些规则。它们在这里定义https://w3c.github.io/webcomponents/spec/custom/#custom-element-conformance

但我要指出的一件事是,您不应该触摸或更改构造函数中的任何属性或子元素。主要是因为在构建 Web Component 时没有属性或任何子元素。这些都是事后更改和添加的。

【讨论】:

  • w3c 链接为 404
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-27
  • 1970-01-01
  • 1970-01-01
  • 2023-03-18
  • 1970-01-01
  • 2022-12-02
相关资源
最近更新 更多