Изоморфные компоненты hyperHTML без прохождения по проводам
У меня есть следующие два компонента:
// component.js
// imports ...
function ListItem(item) {
const html = wire(item)
function render() {
return html`<li>${item.foo}</li>`
}
return render()
}
function List(items) {
const html = wire(items)
function render() {
return html`<ul>${items.map(ListItem)}</ul>`
}
return render()
}
Я хочу поместить их в модуль, который разделяется между клиентом и сервером. Однако, насколько я могу судить, хотя API в значительной степени идентичен, на сервере я должен импортировать функции из модуля viperHTML, на клиенте я должен использовать модуль hyperHTML. Поэтому я не могу просто импортировать функции в верхней части моего общего модуля, но должен перейти к моим компонентам на сайте вызовов.
При этом мой изоморфный компонент будет выглядеть так:
// component.js
function ListItem(html, item) {
//const html = wire(item) // <- NOTE
function render() {
return html`<li>${item.foo}</li>`
}
return render()
}
function List(html, itemHtmls /* :( tried to be consistent */, items) {
//const html = wire(items) // <- NOTE
function render() {
return html`<ul>${items.map(function(item, idx) {
return ListItem(itemHtmls[idx], item)
})}</ul>`
}
return render()
}
Вызов компонентов с сервера:
// server.js
const {hyper, wire, bind, Component} = require('viperhtml')
const items = [{foo: 'bar'}, {foo: 'baz'}, {foo: 'xyz'}]
// wire for the list
const listWire = wire(items)
// wires for the children
const listItemWires = items.map(wire)
const renderedMarkup = List(listWire, listItemWires, items)
Вызов из браузера будет таким же, ожидайте, как будет импортирован hyperhtml:
// client.js
import {hyper, wire, bind, Component} from 'hyperhtml/esm'
Однако писать такой код неприятно, потому что у меня такое ощущение, что результат вызовов wire() должен находиться внутри экземпляров компонента. Есть ли лучший способ написания изоморфных компонентов hyperHTML/viperHTML?
1 ответ
Обновление - теперь есть обходной путь, предоставленный гиперморфным модулем.
В идеальном случае вы имеете зависимость только viperhtml
что в свою очередь приносит hyperhtml
автоматически, как вы можете видеть по файлу index.js.
На этом этапе клиентский пакет должен, если это возможно, растрясти для вас неиспользуемый код, но у вас есть очень хорошее замечание, которое не сразу понятно.
Я также не совсем уверен, могут ли упаковщики быть такими умными, предполагая, что typeof document === "object"
всегда будет верно и только для браузеров.
Один из способов попробовать это
import {hyper, wire, bind, Component} from 'viperhtml'
и на стороне клиента, надеясь, что он не принесет зависимости viperHTML после объединения, потому что в браузере есть много, что вам никогда не понадобится.
У меня такое ощущение, что результат вызовов wire() должен находиться внутри экземпляров компонента.
Вы можете упростить ваши компоненты, используя viper.Component, так что вы будете иметь render() { return this.html... }
и вы забываете об этом, но я согласен с вами, что есть возможности для улучшений.
На этом этапе вам нужно только решить, какие Component
импортировать в одном месте и определять переносимые компоненты, которые работают как на клиенте, так и на сервере.
Это в основном причина света Component
существует в первую очередь, он дает вам свободу сосредоточиться на компоненте, не думая о том, что подключить, как и / или где (если клиент / сервер).
~~ Я собирался показать вам пример, но тот факт, что вы связываете контент с элементом (правильно), заставил меня думать, что текущий Компонент также может быть улучшен, поэтому я создал тикет в качестве продолжения для вашего случая, и я надеюсь, что я буду есть лучшие примеры (для компонентов) раньше, чем позже.~~
редактировать
Я обновил библиотеку, чтобы вы могли создавать компоненты, способные использовать / получать данные / элементы по мере их создания, на примере с кодовым пером.
class ListItem extends Component {
constructor(item) {
super().item = item;
}
render() {
return this.html`<li>${this.item.foo}</li>`;
}
}
class List extends Component {
constructor(items) {
super().items = items;
}
render() {
return this.html`
<ul>${this.items.map(item => ListItem.for(item))}</ul>`;
}
}
Когда вы используете компоненты, вы обеспечиваете их переносимость между клиентом и сервером.
Единственная проблема на этом этапе заключается в том, чтобы выяснить, какой из них лучший Component
учебный класс.
Одним из возможных решений является централизация в одной точке входа экспорта такого класса.
Тем не менее, слон в комнате заключается в том, что NodeJS еще не совместим с модулями ESM, а браузеры не совместимы с CommonJS, поэтому у меня нет лучшего ответа, потому что я не знаю, как / как вы связываете свой код.
В идеале, вы должны использовать CommonJS, который работает из коробки в NodeJS и совместим с каждым браузером, но при этом вам необходимо различать для каждой сборки файл, который экспортирует Component
или любые другие утилиты, связанные с hyper/viperHTML.
Надеюсь, я дал вам достаточно подсказок, чтобы со временем обойти существующие ограничения.
Извиняюсь, если пока у меня нет лучшего ответа. В способе, который я делал ранее, использовались внешние рендеры, но, возможно, это не самый удобный способ использовать более сложные структуры / компоненты.
PS вы могли бы написать эти функции так же, как это
function ListItem(item) {
return wire(item)`<li>${item.foo}</li>`;
}
function List(items) {
return wire(items)`<ul>${items.map(ListItem)}</ul>`;
}