在使用
<template>
和 <slot>
标签定义的自定义组件中,子元素可以获取作为自定义组件的父元素,然后使用此元素引用来访问类定义的变量和函数。
但是,子元素如何知道哪个元素是自定义组件呢?
模板本身包含在影子 DOM 中。当在页面上渲染自定义组件时,这个影子 DOM 会被克隆并附加到自定义组件中作为其宿主。
插入到自定义组件中的元素似乎无法分辨它所附加的节点树来自克隆的影子 DOM 节点,并且无法分辨当前 DOM 树中的哪个元素是顶层自定义组件,除非它迭代每个
parentNode
以检查它们是否有 shadowRoot
。
如何让自定义组件内的子元素访问自定义组件的类变量和函数?
customElements.define("my-comp", class extends HTMLElement {
creationTimestamp;
constructor() {
super();
let template = document.getElementById('my-template');
let templateContent = template.content;
const shadowRoot = this.attachShadow({
mode: 'open'
});
shadowRoot.appendChild(templateContent.cloneNode(true));
this.creationTimestamp = Date.now();
}
})
<html>
<template id="my-template">
I'm a new custom component using slots with value <slot name="slot1">default</slot>
<button onclick="console.log(this.parentNode.host.creationTimestamp)">Print timestamp at Template level</button>
</template>
<my-comp>
<span slot="slot1">
slotted value
<div>
<button onclick="console.log(this.parentNode.parentNode.parentNode.creationTimestamp)">Print timestamp</button>
</div>
</span>
</my-comp>
</html>
有几个非常简单的规则可以解耦 WebComponent:
父级到子级:单向数据绑定或HTML5属性用于传递数据并调用
child.func()
来执行操作。
子级到父级:子级触发事件。父母监听该事件。由于事件冒泡,您可以让任意数量的家长接收该事件,而无需额外的努力。
标准 Web 组件也使用此模式(
input
、textbox
等)。它还允许轻松测试,因为您不必模拟父级。
这是 Polymer 库 描述的推荐模式,它是 WebComponent 的包装器。不幸的是,我找不到描述这种模式的具体文章。
在以下示例中,父级侦听来自子级的事件,并调用自身的函数来影响其状态,或调用子级的函数以使它们执行某些操作。孩子们都不知道父母的事。
这是一种干净的方式,可确保清晰,关注点分离。孩子做他该做的事;父母可以指示它做什么,甚至对其行为做出反应。相反,孩子从不指导父母,甚至不知道。
现在我明白了。您想要防止:
let timeStamp = this.parentNode.parentNode.parentNode.creationTimestamp;
有多个 StackOverflow 答案可以解决这个问题:
让孩子找到家长
控制:孩子
模仿标准 JS
.closest(selector)
,它不会刺穿 ShadowRoots!closestElement(selector)
功能:让父母告诉一些事情给孩子
控制:父母
通过递归深入进入shadowRoots:
如何获取包含shadowRoot元素的文档或节点中的所有HTML
让家长响应孩子的来电
控制:两者子级发送事件,父级需要监听
通过自定义事件通信的Web组件无法发送数据