Есть ли способ / обходной путь для использования принципа слота в 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 . Щелевой элемент дает опору без каркаса.