Контейнеры
Каждое Qwik-приложение содержится внутри элемента, обычно это элемент <html>
. Этот элемент становится контейнером для приложения. Контейнер - это корневой элемент приложения, в котором содержатся все компоненты, состояние и события.
<html q:container="paused" q:version="0.12.1" q:base="/build">
...
</html>
Атрибуты контейнера
Поскольку контейнеры неявно рендерятся средой исполнения Qwik, невозможно определить пользовательские атрибуты HTML с помощью JSX, однако API рендера SSR, такое как renderToString
и renderToStream
, предоставляет опцию containerAttributes
для определения пользовательских атрибутов:
renderToStream(<Root />, {
containerAttributes: {
lang: 'en',
},
});
Приведённый выше код отобразит следующий HTML:
<html lang="en" q:container="paused" q:version="0.12.1" q:base="/build">
...
</html>
В приведенном выше примере атрибут lang
добавляется к элементу контейнера <html>
.
Обратите внимание, что этот атрибут не будет реактивным, если приложению нужно будет динамически изменять это значение, это нужно будет делать самостоятельно с помощью ручной манипуляции DOM.
Наряду с пользовательскими атрибутами, Qwik автоматически рендерит атрибуты q:container
, q:version
, q:render
и q:base
.
-
q:container
- Состояние контейнера. Этот атрибут используется средой выполнения Qwik для определения того, находится контейнер в состоянии паузы или нет. Значением этого атрибута является либоpaused
, либоresumed
. -
q:version
- Версия среды выполнения Qwik. -
q:render
- Указывает, как был отрисован контейнер. Значения этого атрибута: -ssr
,ssr-dev
,dom
илиdom-dev
.
Свойства
Поскольку среда выполнения обеспечивает изоляцию между контейнерами, несколько контейнеров могут сосуществовать в одном документе, контейнер может даже содержать другой вложенный контейнер, это свойство позволяет контейнерам разбивать приложение на более мелкие части.
- возобновлён: Каждый контейнер может быть возобновлён независимо от других компонентов на странице. Независимая возобновляемость ещё больше уменьшает количество состояний, которые десериализует возобновление.
- обновлён: Каждый контейнер может быть обновлён/заменён в любой момент с помощью
innerHTML
. Это позволяет обновить часть страницы, не заставляя заново получать весь HTML-документ, не загружая и не выполняя JavaScript. - скомпилирован: Каждый контейнер может быть скомпилирован и развёрнут отдельно от других контейнеров. Раздельная компиляция особенно полезна для крупномасштабных приложений и больших команд, работающих над приложениями.
- версионирован: Каждый контейнер может запускать различные версии фреймворка Qwik. Это позволяет компоновать веб-сайт из множества небольших контейнеров.
Контейнеры могут быть вложены в дереве и могут взаимодействовать и обмениваться данными. Межкомпонентное взаимодействие требует, чтобы компоненты имели чётко определённые границы, которые мы называем протоколами контейнера.
<html q:container="paused" q:version="0.12.1" q:base="/build">
<head>
<title>Моё Qwik-приложение</title>
</head>
<body>
<header q:container="resumed" q:version="0.11.1" q:base="https://server.a/build">
<div>
<h1>Контейнер заголовка</h1>
</div>
</header>
<footer q:container="paused" q:version="0.13.0" q:base="https://footer.server.b/">
<div>
<h1>Нижний колонтитул</h1>
</div>
</footer>
</body>
</html>
Контейнеры vs. Компоненты
Контейнеры очень похожи на компоненты, в чём же различия? Контейнер можно рассматривать как более ограниченный компонент. Компоненты могут делать несколько вещей, которые не могут делать контейнеры.
- В контейнеры можно передавать параметры, но они доступны только для чтения. Это ограничение связано с тем, что вход контейнера может потребовать сериализации для запросов SSR.
- Контейнеры не понимают слоты - проекцию содержимого.
- Контейнеры не могут изменять состояние, которое было в них передано.
Ограничения компонентов:
- Компоненты должны быть скомпилированы вместе и, как следствие, имеют общий бандл пакета и одинаковую версию Qwik;
- Во время паузы все компоненты в контейнере сериализуются вместе (а затем возобновляются вместе).
Что контейнеры делают?
Контейнеры позволяют нескольким независимым приложениям Qwik работать на странице и вести себя для пользователя как единое приложение. Существует два наиболее распространенных варианта использования:
- Маршрутизация;
- Микро-фронтенд архитектура.
Маршрутизация
Типичный сайт состоит из двух логических частей:
- Навигация, которая, как правило, остаётся неизменной на большинстве страниц.
- Содержимое - это часть страницы, которая изменяется в зависимости от того, на какой маршрут перешел пользователь.
Мы можем смоделировать эти две части как два контейнера: для навигации и содержимого. Когда пользователь впервые переходит к маршруту, сервер отвечает разметкой HTML, которая содержит контейнеры для навигации и содержимого. Когда пользователь переходит на второй маршрут, существует три способа выполнения навигации:
- Упрощенный подход заключается в том, чтобы сделать полный круговорот и загрузить совершенно новую страницу. Основным недостатком является то, что приложение теряет все свои состояния на клиенте.
- Классический подход заключается в том, чтобы обрабатывать любую дальнейшую навигацию в JavaScript. Мы заменяем текущий компонент содержимого на новый компонент содержимого и позволяем новому компоненту отрисоваться. Недостатком является то, что нам необходимо загрузить и выполнить JavaScript.
- В подходе Qwik навигация и содержимое рассматриваются как два разных контейнера. Первый переход загружает HTML, представляющий полную страницу (с двумя контейнерами). При последующей навигации извлекается HTML только для контейнера содержания. Такой подход является лучшим из двух миров. Навигация осуществляется быстро (без загрузки и выполнения JavaScript), а приложение сохраняет свое состояние в родительском контейнере.
Микро-фронтенд
Когда приложение становится очень большим, непрактично рассматривать его как отдельное приложение. Лучшая ментальная модель заключается в том, что много приложений работают вместе, создавая у пользователя впечатление единого приложения.
Для больших приложений команды также становятся большими. Большие команды обычно имеют разные задачи и, как следствие, разные графики релизов.
Контейнеры позволяют большой команде разбить приложение на множество мелких частей и рассматривать каждую часть как единое целое с отдельным графиком развертывания, тестирования и обновления версии.
Команды разбивают приложение на контейнеры и чётко определяют протоколы взаимодействия между контейнерами. Пока протоколы совместимы, каждая команда может развернуть два контейнера независимо друг от друга.