Возможности серверного рендеринга Live Mercury легко переоценить, поэтому этот урок описывает поведение кода. Есть флаг и поведение для рендерера, но рендерер не настроен, а клиент не выполняет гидратацию.
Текущее состояние серверного рендеринга
- У
<.mercury>есть флагssr. Он выключен по умолчанию, а в umbrella-проекте нигде не задан:ssr_module— рендерер, который должен вызываться. - При выключенном SSR элемент
<.mercury>пуст до запуска его JavaScript. - Если элемент содержит серверную разметку, первая клиентская отрисовка заменяет её. Гидратации нет.
- Острова EDU показывают серверный HTML до запуска JavaScript, но это резервная разметка самой страницы, а не результат серверного рендеринга компонента.
- Руководство live_mercury прямо указывает: серверный рендеринг Mercury Hyperscript через LiveMercury не входит в этот релиз.
Флаг ssr
ssr — зарезервированный assign функции mercury/1:
~H"""
<.mercury name="MyComponent" ssr={true} />
"""
Его значение по умолчанию задаёт config :live_mercury, :ssr, читаемый при компиляции; если настройка отсутствует, используется false. Даже включённый флаг действует только при первом, неподключённом рендеринге: когда отслеживание изменений ещё не началось (__changed__ равен nil) и сокет не подключён. Каждый рендеринг при активном подключении игнорирует этот флаг.
Когда флаг действует, mercury/1 вызывает LiveMercury.SSR.render(name, props, slots) с закодированными свойствами. Если вызов поднимает LiveMercury.SSR.NotConfigured, остров рендерится так, будто SSR выключен. Иначе элемент получает data-ssr, а его содержимым становится :html из ответа.
Здесь есть тонкость, закреплённая тестом. Если :ssr_module не настроен, render/3 не вызывает исключение, а возвращает %{preloadLinks: "", html: ""}. Поэтому <.mercury ssr={true}> помечает атрибутом data-ssr элемент с пустым HTML. Именно поэтому флаг выключен по умолчанию: комментарий над @ssr_default описывает такое поведение при прежнем включённом значении.
LiveMercury.SSR и render/3
LiveMercury.SSR — поведение с одним обратным вызовом:
@callback render(component_name, props, slots) :: render_response | no_return
Рендерер — модуль, реализующий это поведение и указанный в конфигурации:
config :live_mercury, ssr_module: MyCustomSSRModule
LiveMercury.SSR.render/3 вызывает настроенный модуль внутри интервала telemetry [:live_mercury, :ssr], метаданные которого содержат имя компонента, свойства и слоты. Строковый ответ разделяется по первому <!-- preload --> на preloadLinks и html; ответ другого типа возвращается как есть, а в элемент попадает только его :html. Шаблон mercury/1 не использует preloadLinks.
В приложении остаются две реализации для проектов с собственным рендерером; обе требуют рендерер вне BEAM:
LiveMercury.SSR.ViteJSотправляет имя, свойства и слоты POST-запросом наhttp://{:vite_host}/ssr_render. LiveMercury не поставляет ни плагин Vite, ни серверный рендерер для этого адреса.LiveMercury.SSR.NodeJSвызывает файлserver.jsчерез пакетNodeJS.
Они поднимают LiveMercury.SSR.NotConfigured, когда не могут обратиться к рендереру: ViteJS — если :vite_host не задан, NodeJS — если пакет NodeJS не загружен или его супервизор не запущен. Ни одна реализация не настроена в этом репозитории, и курс их не использует: они вернули бы среду выполнения JavaScript в путь рендеринга острова.
Остров без SSR
При выключенном SSR элемент <.mercury> изначально пуст. Так настроены все острова этого типа в репозитории. Всё необходимое компоненту передаётся в атрибутах:
- свойства в
data-props, в компактном JSON; - HTML слота по умолчанию в
data-slots, в кодировке base64; клиент отображает его как дочернее содержимое компонента.
Поэтому остров <.mercury> ничего не показывает, пока не загрузится его бандл и MercuryHook не выполнит отрисовку. LiveView как обычно рендерит всё вокруг острова, поэтому страницу можно читать; самому содержимому острова нужен JavaScript.
Без гидратации
При гидратации клиент принимает серверный DOM и подключается к нему. Live Mercury этого не делает. При первой отрисовке элемента с атрибутом data-ssr хук очищает элемент и рендерит компонент заново. Причина описана в исходнике хука: render() из hyperscript повторно использует совпадающие узлы, а остальные оставляет рядом с островом. Без очистки при частичном совпадении серверная разметка и остров отображались бы одновременно.
Тест закрепляет поведение: элемент с содержимым <span>server</span><p>fallback</p> и атрибутом data-ssr после mounted() показывает только остров и содержит один дочерний узел.
Элемент без data-ssr сохраняет существующие дочерние узлы. Так сохраняется скрипт свойств EDU: хук рисует рядом с ним.
Резервная разметка EDU — не SSR компонента
Контейнер EDU EchoEduWeb.Island.island/1 действительно помещает на страницу читаемый HTML: свой внутренний блок, написанный в шаблоне LiveView.
~H"""
<.island id={@island_id} bundle={@bundle} props={%{title: "The demo island"}}>
<p class="mx-text mx-text--muted">The island mounts here when JavaScript runs.</p>
</.island>
"""
Это вручную написанная резервная разметка, а не результат рендеринга компонента. При монтировании острова загрузчик EDU временно убирает её, кроме скрипта свойств, и возвращает те же узлы после завершения работы острова. Тест страницы проверяет, что серверная разметка страницы с островом читается без JavaScript.
Используйте резервную разметку, когда страницу нужно читать без JavaScript. Читатель сначала увидит её, затем остров.
Чего пока нет
- Нет рендерера. Ни один файл конфигурации umbrella-проекта не задаёт
:ssrили:ssr_moduleдля:live_mercury. - Рендерер Mercury не подключён. У
@fiberfx/hyperscriptесть точка входа./server, упакованная как@fiberfx/render-to-stringс функциейrenderToString; также экспортируютсяhydrateиhydrateRootиз клиентской точки входа. Они разобраны в уроке Рендеринг на сервере. Ниecho/apps/live_mercury, ни@fiberfx/live-mercury, ни загрузчик EDU их не вызывают. - Нет гидратации — так устроен текущий хук.
preloadLinksразбирается, но нигде не используется.
Включение серверного рендеринга, как говорит руководство, требует отдельного шага с проверкой результата. Пока он не выполнен, пишите острова, которые корректно рендерятся только из своих свойств, и добавляйте резервную разметку на страницы EDU.