Web 组件:如何使用 children?
Web components: How to work with children?
我目前正在尝试使用 StencilJS 来创建一些 Web 组件。
现在我知道有 <slot />
和命名插槽以及所有这些东西。来自 React,我猜 slot 类似于 React 中的 children。你可以在 React 中使用 children 做很多事情。我经常做的事情:
- 检查是否提供了 children
- 遍历 children 对每个 child 做一些事情(例如,将其包装在 div 和 class 等中)
你会如何使用 slot/web components/stencilJS 来做到这一点?
我可以使用
在 Stencil 中获取我的 Web 组件的宿主元素
@Element() hostElement: HTMLElement;
我像这样使用我的组件
<my-custom-component>
<button>1</button>
<button>2</button>
<button>3</button>
</my-custom-component>
我想渲染类似
的东西
render() {
return slottedChildren ?
<span>No Elements</span> :
<ul class="my-custom-component">
slottedChildren.map(child => <li class="my-custom-element>{child}</li>)
</ul>;
}
亲切的问候
使用插槽,您无需在渲染函数中放置条件。您可以将 no children 元素(在您的示例中为 span)放在 slot 元素内,如果没有向 slot 提供子元素,它将回退到它。
例如:
render() {
return (
<div>
<slot><span>no elements</span></slot>
</div>
);
}
回答你写的评论 - 你可以做这样的事情,但需要一些编码而不是开箱即用。每个插槽元素都有一个 assignedNodes
函数。使用这些知识和对 Stencil 组件生命周期的理解,您可以执行以下操作:
import {Component, Element, State} from '@stencil/core';
@Component({
tag: 'slotted-element',
styleUrl: 'slotted-element.css',
shadow: true
})
export class SlottedElement {
@Element() host: HTMLDivElement;
@State() children: Array<any> = [];
componentWillLoad() {
let slotted = this.host.shadowRoot.querySelector('slot') as HTMLSlotElement;
this.children = slotted.assignedNodes().filter((node) => { return node.nodeName !== '#text'; });
}
render() {
return (
<div>
<slot />
<ul>
{this.children.map(child => { return <li innerHTML={child.outerHTML}></li>; })}
</ul>
</div>
);
}
}
这不是最佳解决方案,它要求插槽的样式应将显示设置为 none(因为您不想显示它)。
此外,它只适用于只需要渲染而不需要事件或其他任何东西的简单元素(因为它只将它们用作 html 字符串而不是对象)。
谢谢吉尔的回答
我之前也在考虑类似的事情(设置状态等 - 因为可能会出现时间问题)。不过我不喜欢该解决方案,因为您随后在 componentDidLoad 中进行状态更改,这将在组件加载后立即触发另一次加载。这看起来很脏而且性能不佳。
虽然 innerHTML={child.outerHTML}
的一点点帮助了我很多。
看来你也可以简单地做:
import {Component, Element, State} from '@stencil/core';
@Component({
tag: 'slotted-element',
styleUrl: 'slotted-element.css',
shadow: true
})
export class SlottedElement {
@Element() host: HTMLDivElement;
render() {
return (
<div>
<ul>
{Array.from(this.host.children)
.map(child => <li innerHTML={child.outerHTML} />)}
</ul>
</div>
);
}
}
我认为您可能 运行 遇到时间问题,因为在 render()
期间,主机的 child 元素已被删除以使 space 任何 render()
returns。但是由于 shadow-dom 和 light-dom 在主机组件中很好地共存,我想应该不会有任何问题。
虽然我真的不知道你为什么要使用 innerHTML
。来自 React,我习惯于这样做:
{Array.from(this.host.children)
.map(child => <li>{child}</li>)}
我认为这是基本的 JSX 语法,而且由于 Stencil 也在使用 JSX,所以我也可以这样做。虽然不起作用。 innerHTML
对我有用。再次感谢。
编辑:如果您不使用 shadow-dom,我提到的时间问题将会出现。一些奇怪的事情开始发生,你最终会得到很多重复的 children。
虽然你可以这样做(可能有副作用):
import {Component, Element, State} from '@stencil/core';
@Component({
tag: 'slotted-element',
styleUrl: 'slotted-element.css',
shadow: true
})
export class SlottedElement {
children: Element[];
@Element() host: HTMLDivElement;
componentWillLoad() {
this.children = Array.from(this.host.children);
this.host.innerHTML = '';
}
render() {
return (
<div>
<ul>
{this.children.map(child => <li innerHTML={child.outerHTML} />)}
</ul>
</div>
);
}
}
我目前正在尝试使用 StencilJS 来创建一些 Web 组件。
现在我知道有 <slot />
和命名插槽以及所有这些东西。来自 React,我猜 slot 类似于 React 中的 children。你可以在 React 中使用 children 做很多事情。我经常做的事情:
- 检查是否提供了 children
- 遍历 children 对每个 child 做一些事情(例如,将其包装在 div 和 class 等中)
你会如何使用 slot/web components/stencilJS 来做到这一点?
我可以使用
在 Stencil 中获取我的 Web 组件的宿主元素@Element() hostElement: HTMLElement;
我像这样使用我的组件
<my-custom-component>
<button>1</button>
<button>2</button>
<button>3</button>
</my-custom-component>
我想渲染类似
的东西render() {
return slottedChildren ?
<span>No Elements</span> :
<ul class="my-custom-component">
slottedChildren.map(child => <li class="my-custom-element>{child}</li>)
</ul>;
}
亲切的问候
使用插槽,您无需在渲染函数中放置条件。您可以将 no children 元素(在您的示例中为 span)放在 slot 元素内,如果没有向 slot 提供子元素,它将回退到它。 例如:
render() {
return (
<div>
<slot><span>no elements</span></slot>
</div>
);
}
回答你写的评论 - 你可以做这样的事情,但需要一些编码而不是开箱即用。每个插槽元素都有一个 assignedNodes
函数。使用这些知识和对 Stencil 组件生命周期的理解,您可以执行以下操作:
import {Component, Element, State} from '@stencil/core';
@Component({
tag: 'slotted-element',
styleUrl: 'slotted-element.css',
shadow: true
})
export class SlottedElement {
@Element() host: HTMLDivElement;
@State() children: Array<any> = [];
componentWillLoad() {
let slotted = this.host.shadowRoot.querySelector('slot') as HTMLSlotElement;
this.children = slotted.assignedNodes().filter((node) => { return node.nodeName !== '#text'; });
}
render() {
return (
<div>
<slot />
<ul>
{this.children.map(child => { return <li innerHTML={child.outerHTML}></li>; })}
</ul>
</div>
);
}
}
这不是最佳解决方案,它要求插槽的样式应将显示设置为 none(因为您不想显示它)。 此外,它只适用于只需要渲染而不需要事件或其他任何东西的简单元素(因为它只将它们用作 html 字符串而不是对象)。
谢谢吉尔的回答
我之前也在考虑类似的事情(设置状态等 - 因为可能会出现时间问题)。不过我不喜欢该解决方案,因为您随后在 componentDidLoad 中进行状态更改,这将在组件加载后立即触发另一次加载。这看起来很脏而且性能不佳。
虽然 innerHTML={child.outerHTML}
的一点点帮助了我很多。
看来你也可以简单地做:
import {Component, Element, State} from '@stencil/core';
@Component({
tag: 'slotted-element',
styleUrl: 'slotted-element.css',
shadow: true
})
export class SlottedElement {
@Element() host: HTMLDivElement;
render() {
return (
<div>
<ul>
{Array.from(this.host.children)
.map(child => <li innerHTML={child.outerHTML} />)}
</ul>
</div>
);
}
}
我认为您可能 运行 遇到时间问题,因为在 render()
期间,主机的 child 元素已被删除以使 space 任何 render()
returns。但是由于 shadow-dom 和 light-dom 在主机组件中很好地共存,我想应该不会有任何问题。
虽然我真的不知道你为什么要使用 innerHTML
。来自 React,我习惯于这样做:
{Array.from(this.host.children)
.map(child => <li>{child}</li>)}
我认为这是基本的 JSX 语法,而且由于 Stencil 也在使用 JSX,所以我也可以这样做。虽然不起作用。 innerHTML
对我有用。再次感谢。
编辑:如果您不使用 shadow-dom,我提到的时间问题将会出现。一些奇怪的事情开始发生,你最终会得到很多重复的 children。 虽然你可以这样做(可能有副作用):
import {Component, Element, State} from '@stencil/core';
@Component({
tag: 'slotted-element',
styleUrl: 'slotted-element.css',
shadow: true
})
export class SlottedElement {
children: Element[];
@Element() host: HTMLDivElement;
componentWillLoad() {
this.children = Array.from(this.host.children);
this.host.innerHTML = '';
}
render() {
return (
<div>
<ul>
{this.children.map(child => <li innerHTML={child.outerHTML} />)}
</ul>
</div>
);
}
}