把自定义元素 和 MutationObservers 结合起来
因为自定义元素还没有被广泛支持,所以必须使用 MutationObservers 来检测 DOM 改变。主要有两种选择。
- 在自定义元素的基础上创建 api ,然后使用 MutationObservers 来作兼容
- 使用 MutationObservers 创建 api ,然后当需要的时候使用自定义元素来添加一些性能改进
我选择后者,因为 MutationObservers 是检测子元素改变的必要条件,即使在完全支持自定义元素的浏览器中也是如此。
我为下一版本的 NX 使用的系统简单地在旧浏览器的文档添加一个 MutationObserver。然而在现代浏览器中,该系统使用自定义元素为最顶层的组件创建钩子,并且在他们的 connectedCallback
钩子中添加一个 MutationObserver
。这个 MutationObserver 可以用来扮演在组件内部检测进一步的 DOM 变化的角色。
它只查找文档中由框架控制的部分中的更改。对应的代码大概如下。
function registerRoot (name) {
if ('customElements' in window) {
registerRootV1(name)
} else if ('registerElement' in document) {
registerRootV0(name)
} else {
// add a MutationObserver to the document
}
}
function registerRootV1 (name) {
function RootElement () {
return Reflect.construct(HTMLElement, [], RootElement)
}
const proto = RootElement.prototype
Object.setPrototypeOf(proto, HTMLElement.prototype)
Object.setPrototypeOf(RootElement, HTMLElement)
proto.connectedCallback = connectedCallback
proto.disconnectedCallback = disconnectedCallback
customElements.define(name, RootElement)
}
function registerRootV0 (name) {
const proto = Object.create(HTMLElement)
proto.attachedCallback = connectedCallback
proto.detachedCallback = disconnectedCallback
document.registerElement(name, { prototype: proto })
}
function connectedCallback (elem) {
// add a MutationObserver to the root element
}
function disconnectedCallback (elem) {
// remove the MutationObserver from the root element
}
这会为现代浏览器带来性能的好处,因为他们只需处理极少的 DOM 变化。