有没有办法/解决方法在hyperHTML中使用插槽原则而不使用Shadow DOM?

时间:2018-02-11 00:01:28

标签: hyperhtml

我喜欢使用'标记模板文字'标记模板文字的hyperHtml和lit-html的简单性。只更新变量部分'的模板。简单的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:

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

3 个答案:

答案 0 :(得分:7)

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

刚刚停放的数据

在你的布局中有插槽是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来保证样式和节点免受不良页面/第三部分污染的影响,我们可以按照基于自定义元素的this Code Pen example所示进行操作。

正如您所看到的,唯一需要的API是attachShadow,并且有一个超级lightweight polyfill for just that,重量为1.6K min-zipped。

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

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

答案 1 :(得分: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>

答案 2 :(得分:0)

多个实用程序和框架支持没有 shadow dom 的插槽。 Stencil 允许使用 without shadow DOM enabledslotted-element 提供无框架支持。