Есть ли способ / обходной путь для использования принципа слота в hyperHTML без использования Shadow DOM?

Мне нравится простота hyperHtml и lit-html, которые используют "теги с литералами шаблона" только для обновления "переменных частей" шаблона. Простой javascript, не требующий виртуального кода DOM и рекомендуемого неизменяемого состояния.

Я хотел бы попытаться использовать пользовательские элементы с HyperHtml как можно проще с поддержкой <slot/> Принцип в шаблонах, но без Shadow DOM. Если я правильно понимаю, слоты возможны только с Shadow DOM?

Есть ли способ или обходной путь, чтобы иметь <slot/> принцип в hyperHTML без использования Shadow DOM?

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

Хотя есть и преимущества, по некоторым причинам я предпочитаю не использовать Shadow DOM:

  • Я хочу посмотреть, смогу ли я преобразовать свой существующий SPA: все необходимые стили CSS теперь хранятся в файлах SASS и скомпилированы в 1 файл CSS. Использование глобального CSS внутри компонентов Shadow DOM не легко, и я предпочитаю не распутывать SASS (сейчас)
  • Shadow DOM имеет некоторую стоимость производительности
  • Я не хочу, чтобы в большом полифиле Shadow DOM были слоты ( webcomponents-lite.js: 84KB - unminified)

3 ответа

Решение

Позвольте мне начать описывать, что такое слоты и какие проблемы они решают.

Только что припаркованные данные

Наличие слотов в макете - это попытка 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 для защиты стилей и узлов от нежелательного загрязнения страниц / 3-х частей, мы можем сделать это, как показано в этом примере Code Pen, на основе пользовательских элементов.

Как вы можете видеть, единственный необходимый API - это метод attachShadow, и для этого существует сверхлегкий полифилл, который весит всего 1,6 тыс. Мин.

И последнее, но не менее важное: вы можете использовать слоты внутри литералов шаблона HyperHTML и позволить браузеру выполнять преобразование, но для этого потребуются более тяжелые полифилы, и я бы не рекомендовал его использовать в производстве, особенно когда есть лучшие и более легкие альтернативы, как показано здесь.

Я надеюсь, что этот ответ помог вам.

У меня есть похожий подход, я создал базовый элемент (из HyperElement), который проверяет дочерние элементы внутри пользовательского элемента в конструкторе, если элемент не имеет атрибута slot, я просто отправляю их в слот по умолчанию

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>

Слоты без теневого домена поддерживаются множеством утилит и фреймворков. Stencil позволяет использовать без включенной теневой модели DOM . Щелевой элемент дает опору без каркаса.

Другие вопросы по тегам