7

我喜欢 hyperHtml 和 lit-html 的简单性,它们使用“标记模板文字”来仅更新模板的“可变部分”。简单的 javascript,不需要虚拟 DOM 代码和推荐的不可变状态。

我想尝试在模板中支持原则的情况下尽可能简单地使用带有 hyperHtml 的自定义元素<slot/>,但没有 Shadow DOM。如果我理解正确,插槽只能使用 Shadow DOM?

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

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

虽然有好处,但我不喜欢使用 Shadow DOM 的一些原因:

  • 我想看看我是否可以转换我现有的 SPA:所有必需的 CSS 样式现在都存在于 SASS 文件中,并被编译为 1 个 CSS 文件。在 Shadow DOM 组件中使用全局 CSS并不容易,我不想解开 SASS(现在)
  • Shadow DOM 有一些性能成本
  • 我不希望大型 Shadow DOM polyfill 有插槽(webcomponents-lite.js:84KB - 未缩小)
4

3 回答 3

8

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

刚刚停放的数据

在你的布局中有槽是 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,
          ($0, $1) => $1.toUpperCase())
        ] = el; // <- this is a DOM node, not a string ;-)
      });
      return data;
    }

插槽作为 hyperHTML 插值

现在我们有了解决插槽的方法,我们所需要的只是一种将它们放置在布局中的方法。

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

    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 来保护样式和节点免受不希望的页面/第三部分污染,我们可以按照基于自定义元素的代码笔示例中所示的方式进行操作。

正如你所看到的,唯一需要的 API 是 attachShadow ,并且有一个超轻量级的 polyfill,它的最小压缩重量为 1.6K。

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

我希望这个答案对你有所帮助。

于 2018-02-12T08:05:47.370 回答
2

我有一个类似的方法,我创建了一个基本元素(来自 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>
于 2018-02-12T22:56:58.740 回答
0

多个实用程序和框架支持没有 shadow dom 的插槽。Stencil 可以 在不启用 shadow DOM 的情况下使用。slotted-element在没有框架的情况下提供支持。

于 2021-03-25T17:09:07.830 回答