如何使用 TSX 使 StencilJS 组件在没有组件标签本身的情况下呈现?

How can I make StencilJS component to render without component tag itself with TSX?

虽然我知道这可能是一种糟糕的做法,但我需要构建 StencilJS 组件,以便在 render() 内部,我不想渲染组件标签本身,因为已经存在样式指南并且它期望 DOM 以某种方式构建。这是我要实现的目标 - 组件代码(来自 HTML 或在另一个组件内):

<tab-header-list>
  <tab-header label="tab 1"></tab-header>
  <tab-header label="tab 2"></tab-header>
</tab-header-list>

渲染时,我希望生成的 DOM 类似于:

<tab-header-list>
  <ul>
    <li>tab 1</li>
    <li>tab 2</li>
  </ul>
</tab-header-list>

所以在 tab-header-list render() 函数中,我正在做

return (
  <ul>
    <slot/>
  </ul>
);

我可以在 tab-header render() 函数中执行此操作

@Element() el: HTMLElement;
@Prop() label: string;

render() {
  this.el.outerHTML = `<li>${this.label}</li>`;
}

得到我想要的,但我如何使用 TSX 做到这一点? (为简单起见,上面的代码非常简单,但我真正需要构建的是带有事件等的更复杂的 li 标签,所以我想使用 TSX)

试图将 DOM 存储到变量,但我不确定如何将其分配为 this.el(外部 HTML 似乎是我能想到的唯一方法,但我觉得一定有更好的方法)

@Element() el: HTMLElement;
@Prop() label: string;

render() {
  var tabheaderDOM = (<li>{this.label}</li>);

  // how can I assign above DOM to this.el somehow?
  //this.el.outerHTML = ?
}

非常感谢能得到的任何帮助 - 提前感谢您的宝贵时间!

遗憾的是,您不能使用没有标签的自定义元素,但有一个解决方法:

您可以使用 Host 元素作为结果标签的参考。

render () {
  return (
    <Host>....</Host>
  )
}

然后在你的样式表中你可以为它设置 display 属性:

:host {
  display: contents;
}

display: contents causes an element's children to appear as if they were direct children of the element's parent, ignoring the element itself

注意:它在 IE、opera mini 中不起作用...https://caniuse.com/#feat=css-display-contents

更新:

如果您不使用 shadowDOM,则需要将 :host 替换为标签名称,例如:

tab-header {
  display: contents;
}

功能组件或许可以帮助您实现这一目标。它们只是 return 作为 TSX 元素的函数的语法糖,因此它们与普通的 Stencil 组件完全不同。主要区别在于它们不编译为 Web 组件,因此只能在 TSX 中工作。但它们也不会产生额外的 DOM 节点,因为它们只是 return 函数 returns.

的模板

让我们举个例子:

@Element() el: HTMLElement;
@Prop() label: string;

render() {
  this.el.outerHTML = `<li>${this.label}</li>`;
}

你可以把它写成一个功能组件:

import { FunctionalComponent } from '@stencil/core';

interface ListItemProps {
  label: string;
}

export const ListItem: FunctionalComponent<ListItemProps> = ({ label }) => (
  <li>{label}</li>
);

然后你就可以像

一样使用它了
import { ListItem } from './ListItem';

@Component({ tag: 'my-comp' })
export class MyComp {
  render() {
    return (
      <ul>
        <ListItem label="tab 1" />
        <ListItem label="tab 2" />
      </ul>
    );
  }
}

将呈现为

<ul>
  <li>tab 1</li>
  <li>tab 2</li>
</ul>

除了标签道具,您还可以编写功能组件来接受标签作为子组件:

export const ListItem: FunctionalComponent = (_, children) => (
  <li>{children}</li>
);

并像

一样使用它
<ListItem>tab 1</ListItem>

BTW Host 实际上是一个函数组件。要了解有关功能组件(及其限制)的更多信息,请参阅 https://stenciljs.com/docs/functional-components