把自定义元素 和 MutationObservers 结合起来

因为自定义元素还没有被广泛支持,所以必须使用 MutationObservers 来检测 DOM 改变。主要有两种选择。

  • 在自定义元素的基础上创建 api ,然后使用 MutationObservers 来作兼容
  • 使用 MutationObservers 创建 api ,然后当需要的时候使用自定义元素来添加一些性能改进

我选择后者,因为 MutationObservers 是检测子元素改变的必要条件,即使在完全支持自定义元素的浏览器中也是如此。

我为下一版本的 NX 使用的系统简单地在旧浏览器的文档添加一个 MutationObserver。然而在现代浏览器中,该系统使用自定义元素为最顶层的组件创建钩子,并且在他们的 connectedCallback 钩子中添加一个 MutationObserver。这个 MutationObserver 可以用来扮演在组件内部检测进一步的 DOM 变化的角色。

它只查找文档中由框架控制的部分中的更改。对应的代码大概如下。

  1. function registerRoot (name) {
  2. if ('customElements' in window) {
  3. registerRootV1(name)
  4. } else if ('registerElement' in document) {
  5. registerRootV0(name)
  6. } else {
  7. // add a MutationObserver to the document
  8. }
  9. }
  10. function registerRootV1 (name) {
  11. function RootElement () {
  12. return Reflect.construct(HTMLElement, [], RootElement)
  13. }
  14. const proto = RootElement.prototype
  15. Object.setPrototypeOf(proto, HTMLElement.prototype)
  16. Object.setPrototypeOf(RootElement, HTMLElement)
  17. proto.connectedCallback = connectedCallback
  18. proto.disconnectedCallback = disconnectedCallback
  19. customElements.define(name, RootElement)
  20. }
  21. function registerRootV0 (name) {
  22. const proto = Object.create(HTMLElement)
  23. proto.attachedCallback = connectedCallback
  24. proto.detachedCallback = disconnectedCallback
  25. document.registerElement(name, { prototype: proto })
  26. }
  27. function connectedCallback (elem) {
  28. // add a MutationObserver to the root element
  29. }
  30. function disconnectedCallback (elem) {
  31. // remove the MutationObserver from the root element
  32. }

这会为现代浏览器带来性能的好处,因为他们只需处理极少的 DOM 变化。