Skip to content

SSR-рендеринг ​

Masonry-раскладка зависит от реально измеренных размеров, которых не существует на сервере — и не существует ещё один клиентский кадр до первого измерения движка. Вместо того чтобы рендерить пустоту (или вспышку нестилизованного/наложенного друг на друга содержимого) в этом окне, каждый адаптер вместо этого рендерит приближение через CSS columns, а затем сменяет его на точную transform-раскладку в тот самый момент, когда движок реально измеряет карточки — в том же кадре, без заметной вспышки.

Число колонок для SSR-приближения ​

MasonryOptions.ssrColumns

ssrColumns ​

number · опционально

Явное число колонок для чистого CSS-приближения columns, которое адаптер фреймворка рендерит до появления движка. Сам движок никогда не читает это поле — оно резолвится через экспортируемый ядром хелпер resolveSsrColumns() и целиком используется адаптером, так что задавать его имеет смысл, даже несмотря на то, что сам createMasonryEngine() с ним ничего не делает.

Порядок разрешения (через resolveSsrColumns(spec, explicit)):

  1. Явный ssrColumns побеждает всегда.
  2. Иначе, если columns (или rows, при direction: 'horizontal') уже является конкретным number или объектом с брейкпоинтами — измерение для их разрешения не требуется — значение переиспользуется как есть (объект с брейкпоинтами резолвится в своё значение default, поскольку реальная ширина контейнера ещё не известна).
  3. 'auto' или не заданное значение откатывается к консервативному дефолту 2 — безопасно и на мобильной, и на десктопной ширине, никогда неудобно узко.
ts
const engine = createMasonryEngine(container, {
  columns: 'auto', // реальное число колонок разрешится, как только контейнер реально измерят
  ssrColumns: 3, // но до этого рендерить приближение через CSS columns на 3 колонки
})

От CSS-приближения к реальной раскладке ​

До первого срабатывания события layout движка <MasonryGrid> рендерит свой корневой элемент с:

css
columns: <разрешённый ssrColumns>;
column-gap: <разрешённый поперечный отступ>px;

а каждая обёртка карточки получает break-inside: avoid плюс margin-bottom: <главный отступ>px — прилично выглядящее, пусть и не попиксельно точное, приближение masonry-раскладки исключительно средствами CSS, которые браузер уже понимает, без единой строчки JavaScript. Именно это рендерится во время SSR (нет window, нет движка вообще) и один клиентский кадр до завершения первого реального измерения при гидратации.

В момент первого срабатывания события layout движка адаптер полностью убирает этот CSS (не остаётся ни columns, ни column-gap, ни break-inside) и переключает каждую карточку на реальную расстановку через position: absolute + transform: translate() — управляется булевым флагом (hasLaidOut), который собственная реактивность Vue/React применяет ещё до следующей отрисовки, так что нет ни одного видимого кадра, где присутствовали бы обе раскладки одновременно или где содержимое дёргалось бы между ними.

Обёртку вроде <ClientOnly>/паттерн useIsomorphicLayoutEffect нигде не нужно добавлять самостоятельно — оба адаптера, и Vue, и React, уже сами обрабатывают разделение сервер/клиент внутри (собственный useLayoutEffect React предупреждает, если срабатывает на сервере, поэтому адаптер сам откатывается там на useEffect).