Изоморфные компоненты 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>`;
}
Другие вопросы по тегам