Прошло некоторое время с тех пор, как я написал статью о SolidJS технологические инновации. Прошло два года с тех пор, как мы добавили Suspense на сервер с Streaming SSR. И даже больше, чтобы вернуться к тому времени, когда мы впервые представили Suspense для выборки данных и одновременного рендеринга еще в 2019 году.

В то время как React представил эти концепции, их реализация для мелкозернистой реактивной системы была совершенно другим зверем. Требует немного воображения и совершенно разных решений, позволяющих избежать различий.

И это похоже на исследование, которое мы недавно проводили. Вдохновленный равными частями из Компоненты сервера React и островные решения, такие как Марко а также Астро, Solid сделал первые шаги в частичную гидратацию. (сравнение внизу)



SolidStart

Описание изображения

С момента выпуска Solid 1.0 я был немного завален. Между сдерживанием открытых проблем и попыткой поставить больше галочек для принятия я определенно чувствовал себя истощенным. Все указывало на потребность в мета-фреймворке SSR, который я начал еще до выпуска 1.0.

Сообщество пришло на помощь. Но в конечном счете, для выпуска бета-версии я стал блокировщиком. И Нихил Сараф, никогда не сидящий на месте, недавно познакомившийся с Свежий хотел посмотреть, нельзя ли просто добавить острова в SolidStart.

Желая сосредоточиться на релизе, я согласился, но сказал ему ограничить время, так как мне понадобится его помощь на следующий день. На следующий день он показал мне демонстрацию, в которой он не только добавил острова, воссоздав интерфейс Fresh, но и добавил маршрутизацию на стороне клиента.



Случайные острова

Описание изображения

Демонстрация была грубой, но впечатляющей. Он взял одну из моих демонстраций Hackernews и переделал рекурсивные острова. Что такое рекурсивные острова… вот когда вы проектируете острова на островах:

function MyServerComponent(props) {
  return <>{ props.data && 
    <MyClientIsland>
      <MyServerComponent data={props.data.childData} />
    </MyClientIsland>
  }</>
}
Войти в полноэкранный режим

Выйти из полноэкранного режима

Зачем тебе это? Ну, есть правило с островами, что вы не можете импортировать и использовать только серверные компоненты в них. Причина в том, что вы не хотите, чтобы клиент мог передавать им состояние. Почему? Что ж, если бы клиент мог передать им состояние, тогда им нужно было бы иметь возможность обновляться, и, поскольку идея состоит в том, чтобы не отправлять этот JavaScript в браузер, это не сработает. К счастью props.children довольно хорошо обеспечивает соблюдение этой границы. (Предполагая, что вы запрещаете передачу функций рендеринга/реквизитов рендеринга через границы острова).

function MyClientIsland() {
  const [state, setState] = createSignal();

  // can't pass props to the children
  return <div>{props.children}</div>
}
Войти в полноэкранный режим

Выйти из полноэкранного режима

Как ему удалось сделать это демо в такой короткий срок? Ну, это было случайно. Гидратация Solid работает за счет сопоставления иерархических идентификаторов с шаблонами, созданными в DOM. Они выглядят примерно так:

<div data-hk="0-0-1-0-2" />
Войти в полноэкранный режим

Выйти из полноэкранного режима

Каждый шаблон увеличивает счетчик, а каждый вложенный компонент добавляет еще одну цифру. Это необходимо для нашей однопроходной гидратации. В конце концов, JSX можно создавать в любом порядке, а границы Suspense разрешать в любое время.

Но на заданной глубине все id будут присваиваться в том же порядке клиенту или серверу.

function Component() {
  const anotherDiv = <div data-hk="1" /> 
  return <div data-hk="2">{anotherDiv}</div>
}

// output
<div data-hk="2">
  <div data-hk="1" />
</div>
Войти в полноэкранный режим

Выйти из полноэкранного режима

Кроме того, я добавил <NoHydration> компонент для подавления этих идентификаторов, чтобы мы могли пропустить гидратацию ресурсов, таких как ссылки и таблицы стилей в голове. Вещи, которые работали только на сервере и не требовали запуска в браузере.

А также несвязанные, работая над интеграцией Solid с Astro, я добавил механизм для установки префикса для корней гидратации, чтобы предотвратить дублирование этих идентификаторов для несвязанных островов.

Мне просто никогда не приходило в голову, что мы можем использовать наши собственные идентификаторы в качестве префикса. И поскольку он будет просто добавляться в конец, мы можем гидратировать страницу Solid, отображаемую сервером, начиная с любой точки страницы. С <NoHydration> мы могли бы прекратить гидратацию в любой момент, чтобы изолировать дочерние элементы как серверные.



Гибридная маршрутизация

Описание изображения

Для всех преимуществ островов и частичной гидратации, чтобы не отправлять весь JavaScript, вам не нужно требовать этот код в браузере. В тот момент, когда вам нужно отобразить страницы клиента, вам понадобится весь код для отображения следующей страницы.

Хотя такие технологии, как Турбо использовались для извлечения и замены HTML без полной перезагрузки страницы, люди отмечали, что это часто казалось неуклюжим.

Но некоторое время назад у нас была идея, что мы можем взять нашу вложенную маршрутизацию и заменить только партиалы HTML. Еще в марте Райан Тернквист (один из создателей Solid Router) сделал это демо. Хотя визуальной демонстрации было немного, мы доказали, что у нас может быть такая функциональность всего с 1,3 КБ JavaScript.

Хитрость заключалась в том, что с помощью делегирования событий кликов мы могли активировать клиентский маршрутизатор без гидратации страницы. Оттуда мы могли бы использовать AJAX для запроса следующей страницы и передачи предыдущей страницы, и сервер точно знал бы из определения маршрута, какие вложенные части страницы ему нужно отобразить. С возвращенным HTML маршрутизатор на стороне клиента может поменять содержимое.



Завершение картины

Первоначальная демонстрация была грубой, но подавала большие надежды. У него все еще была проблема с двойными данными для серверного контента, и это было то, что нам нужно было решить в ядре. Поэтому мы добавили определение того, когда Solid Resource был создан в части страницы, предназначенной только для сервера. Мы знали, что если то, что запускает выборку данных, может происходить только на сервере, нет необходимости сериализовать все это. Острова уже сериализовали свой реквизит.

Мы также воспользовались этой возможностью, чтобы создать механизм для передачи реактивного контекста через hydrate вызовы, позволяющие Context работать в браузере между островами, разделенными контентом сервера.

С ними мы были готовы к демонстрации рекурсивных комментариев Hackernews:

Но было одно, чего нам не хватало. Замена HTML была хороша для новой навигации, но что делать, если вам нужно обновить часть страницы? Вы бы не хотели терять состояние клиента, фокус ввода и т. д. Nikhil управлял версией, которая это делала. Но в конечном итоге мы использовали микроморф легкая разница DOM, написанная Нейтом Муром (из Астро).

И вместе с этим мы портировали демо-версию приложения Taste movie в его 13-килобайтном JS-славе. (Благодаря мягкому подталкиванию Адди Османи и отличной работе Нихила, Дэвида и нескольких членов сообщества Solid: dev-rb, Мухаммеда Заки, Паоло Риччиути и других).

Страница поиска особенно демонстрирует перезагрузку без потери состояния клиента. Когда вы вводите, ввод не теряет фокус, даже если ему необходимо обновить всю вложенную панель.

Демонстрация солидных фильмов
И дальше Гитхаб

Просто чтобы дать вам представление о том, насколько это абсурдно мало. Это полный переход JavaScript между двумя страницами со списками фильмов, а затем переход к фильму в различных средах с маршрутизацией на стороне клиента из https://tastejs.com/movies/.

Примечание: Только демо Solid использует частичные рендеринг на сервере, так что это немного неравное сравнение. Но смысл в том, чтобы подчеркнуть разницу в размерах. Другие фреймворки работают над аналогичными решениями, такими как RSC в Следующий и контейнеры в Быстрыйно это демо, которые доступны сегодня.



Вывод

Чем больше приложений мы создадим таким образом, тем больше я буду в восторге от этой технологии. Это похоже на одностраничное приложение во всех отношениях, но оно значительно меньше. Честно говоря, я сам удивляюсь каждый раз, когда открываю вкладку сети.

Мы все еще работаем над тем, чтобы вывести это из экспериментального режима и укрепить API. И есть больше возможностей для оптимизации на стороне серверного рендеринга, но мы думаем, что здесь есть все задатки нового типа архитектуры. И это довольно круто.

Следите за нашим прогрессом в этой функции здесь.