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

Posted

技术标签:

【中文标题】如何使用 TSX 使 StencilJS 组件在没有组件标签的情况下呈现?【英文标题】:How can I make StencilJS component to render without component tag itself with TSX? 【发布时间】:2020-11-12 08:11:08 【问题描述】:

虽然我知道这可能是一种糟糕的做法,但我需要构建 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(outerHTML 似乎是我能想到的唯一方法,但我觉得必须有更好的方法)

@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 = ?

感谢您提供的任何帮助 - 提前感谢您的宝贵时间!

【问题讨论】:

【参考方案1】:

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

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

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

然后在您的样式表中,您可以为其设置display 属性:

:host 
  display: contents;

display: contents 使元素的子元素看起来好像是元素父元素的直接子元素,忽略元素本身

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

UPD:

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

tab-header 
  display: contents;

【讨论】:

感谢您的快速回复。我忘了告诉你我正在使用“shadow:false” - 因为我需要反映样式指南的样式(作为全局css加载)所以你的解决方案在这种情况下似乎不起作用(完全是我的错 -我应该首先在我的问题中添加它)现在我重新考虑我正在尝试做的事情,也许它只是超级混乱 - 所以很可能我应该只是在标签标题列表中构建标签标题和根据需要使用 dataObject 自定义选项卡标题。再次感谢您如此详细的快速回复! 啊,没什么大不了的。您需要将:host 替换为标签名称。 更新了我的初始答案 谢谢!使用 ... 将 TSX 组件包围在选项卡标题内似乎可以解决问题!【参考方案2】:

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

让我们举个例子:

@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。

【讨论】:

在这里,我想我想继续使用组件,因为列表项确实会生成 DOM 并且实际的“li”标签非常复杂,所以不要将它隐藏在功能组件下,而是使用“显示:内容”作为公认的答案似乎是我正在寻找的。顺便说一句,我真的很喜欢你在另一个问题中的回答,我确实接受了这个答案,所以再次感谢你的出色解决方案——你肯定向我展示了功能组件的强大功能! :)

以上是关于如何使用 TSX 使 StencilJS 组件在没有组件标签的情况下呈现?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Storybook React App 中嵌入 StencilJS 组件?

StencilJS - 更新导致整个组件重新渲染的状态

stenciljs 学习四 组装饰器

Font Awesome 在 Stenciljs 组件中不起作用

stenciljs 学习八 组件测试

stenciljs 学习七 路由