有没有一个way/workaround在不使用ShadowDOM的情况下在hyperHTML中有插槽原理?

Is there a way/workaround to have the slot principle in hyperHTML without using Shadow DOM?

我喜欢 hyperHtml 和 lit-html 的简单性,它们使用 'Tagged Template Literals' 仅更新模板的 'variable parts'。简单 javascript,不需要虚拟 DOM 代码和推荐的不可变状态。

我想尝试尽可能简单地将自定义元素与 hyperHtml 结合使用 在模板中支持 <slot/> 原则,但没有 Shadow DOM。如果我没看错的话,插槽只能用 Shadow DOM?

有没有一种方法或解决方法可以在不使用 Shadow DOM 的情况下在 hyperHTML 中使用 <slot/> 原则?

    <my-popup>
      <h1>Title</h1>
      <my-button>Close<my-button>
    </my-popup>

虽然有好处,但有些原因我不想使用 Shadow DOM:

让我开始描述什么是槽以及它们解决的问题。

刚刚停放数据

在您的布局中设置插槽是 HTML 尝试让您在布局中放置一些数据,稍后通过 JavaScript 解决它。

你甚至不需要 Shadow DOM 来使用槽,你只需要一个带有命名槽的模板,它将把值放在适当的位置。

    <user-data>
      <img  src="..." slot="avatar">
      <span slot="nick-name">...</span>
      <span slot="full-name">...</span>
    </user-data>

你能找出该组件与以下组件之间的区别吗 JavaScript?

    const userData = {
      avatar: '...',
      nickName: '...',
      fullName: '...'
    };

换句话说,使用像下面这样的函数,我们已经可以将槽转换为由属性寻址的有用数据。

    function slotsAsData(parent) {
      const data = {};
      parent.querySelectorAll('[slot]').forEach(el => {
        // convert 'nick-name' into 'nickName' for easy JS access
        // set the *DOM node* as data property value
        data[el.getAttribute('slot').replace(
          /-(\w)/g,
          ([=12=], ) => .toUpperCase())
        ] = el; // <- this is a DOM node, not a string ;-)
      });
      return data;
    }

插槽作为超HTML插值

既然我们已经找到了解决插槽的方法,我们所需要的就是将它们放置在我们的布局中的方法。

理论上,我们不需要自定义元素即可实现。

    document.querySelectorAll('user-data').forEach(el => {
      // retrieve slots as data
      const data = slotsAsData(el);
      // place data within a more complex template
      hyperHTML.bind(el)`
        <div class="user">
          <div class="avatar">
            ${data.avatar}
          </div>
          ${data.nickName}
          ${data.fullName}
        </div>`;
    });

但是,如果我们想使用 Shadow DOM 来保护样式和节点免受不需要的页面/第 3 部分污染,我们可以根据自定义元素按照 this Code Pen example 中所示进行操作.

如您所见,唯一需要的 API 是 attachShadow,还有一个超级 lightweight polyfill for just that,最小压缩重量为 1.6K。

最后但并非最不重要的一点是,您可以在 hyperHTML 模板文字中使用插槽并让浏览器进行转换,但这需要更重的 polyfill,我不建议在生产中使用它,特别是当存在是更好更轻便的替代品,如此处所示。

希望这个回答对您有所帮助。

我有类似的方法,我创建了一个基本元素(来自 HyperElement),它检查构造函数中自定义元素内的子元素,如果该元素没有插槽属性,我只是将它们发送到默认插槽

    import hyperHTML from 'hyperhtml/esm';

    class HbsBase extends HyperElement {
        constructor(self) {
            self = super(self);
            self._checkSlots();
        }
        _checkSlots() {
            const slots = this.children;
            this.slots = {
                default: []
            };
            if (slots.length > 0) {
                [...slots].map((slot) => {
                     const to = slot.getAttribute ? slot.getAttribute('slot') : null;
                    if (!to) {
                         this.slots.default.push(slot);
                    } else {
                        this.slots[to] = slot;
                    }
                 })
            }
        }
    }

自定义元素,我正在使用自定义汇总插件加载模板

    import template from './customElement.hyper.html';
    class CustomElement extends HbsBase {
        render() {
            template(this.html, this, hyperHTML);
        }
    }

然后在模板上customElement.hyper.html

    <div>
         ${model.slots.body}
    </div>

使用元素

    <custom-element>
       <div slot="body">
         <div class="row">
             <div class="col-sm-6">
                 <label for="" class="">Name</label>
                 <p>
                      <a href="#">${model.firstName} ${model.middleInitial}      ${model.lastName}</a>
                 </p>
             </div>
         </div>
         ...
       </div>
    </custom-element>

没有阴影的插槽 dom 受多个实用程序和框架的支持。 Stencil 启用 without shadow DOM enabled. slotted-element 无需框架即可提供支持。