Feat/suspense module mounter#532
Open
heymdall-legal wants to merge 23 commits into
Open
Conversation
- fetchResources adopts server-emitted module styles (data-module-ssr-href) instead of removing and re-fetching them, avoiding FOUC - add createEmbeddedModuleFetcher: reads resources from embedded payload - useModuleMounter/createLazyMounter call module.update() on runParams change when available, falling back to remount otherwise - export MODULE_SSR_HREF_ATTRIBUTE
Isomorphic component exported from @alfalab/scripts-modules/ssr. - server: suspends on getModuleResources with ssr flag, inlines module styles, renders module html into an outlet, embeds resources payload - client: reads embedded payload (no duplicate resources request), hydrates server markup via module.hydrate (or mounts when absent), calls module.update on runParams change, cleans up on unmount - client loader is built internally wrapping the fetcher in createEmbeddedModuleFetcher, so the resources endpoint is not called twice - add exports map (root '.' unchanged, new './ssr' subpath) - export MODULE_SSR_ROOT_ATTRIBUTE and MODULE_SSR_MOUNT_ID_ATTRIBUTE - round-trip test: renderToPipeableStream -> serialize -> jsdom hydrate
Document in the ssr spec why createSsrMounter shipped without the fallback prop (server/client hydration-consistency problem), the process-global suspense cache trade-offs (cross-request sharing, no abort wiring), and the unused link-mode style adoption path. Also note that the factory builds its client loader internally, superseding the §5.3 passed-in-loader signature.
🦋 Changeset detectedLatest commit: 331ff34 The changes in this PR will be included in the next version bump. This PR includes changesets to release 3 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Contributor
|
Сложно полноценно в голове скомпилировать работу и что-то дельное найти, выглядит классно |
qrik116
approved these changes
Jul 10, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Добавлена возможность монтировать модули через суспенс. Как на клиенте, так и на сервере.
Для серверного монтирования есть дополнительные ограничения, поэтому он реализован отдельным методом.
По сути это все добавление новой фичи и никак не ломает предыдущее использование.
Общая логика описана в
docs/specs/ssr-spec.md. Собственно по этой спеке все и делалось.Единственная реальная "недаработка" в спеке - шареный кеш для суспенса, который чисто теоретически может некорректно пошарится между разными запросами если модули из разных запросов используют абсолютно одинаковый набор параметров для модулей. Буду лечить, но отдельным пр-ом. По сути риск подобного сценария минимален.
В целом тут больше текста и тестов, чем самого реального кода.
Что в реальности реализовано:
В
script-modules:createSsrMounter- изоморфная фабрика компонента для запихивания его в suspense. Оно уже умеет рендрится на сервере и затем корректно гидрируется на клиенте без перезапроса getModuleResources. Этот вариант НЕ умеет работать с shadowDOM.Остальные изменения в пакете по сути позволяют всему этому работать.
В
scripts-server:В
scripts:Как вообще работает подключение модулей с SSR:
На сервере хоста
createSsrMounterрендеритServerModuleвнутри<Suspense>. React приостанавливает рендер, пока не разрешится Suspense-ресурс.Suspense-ресурс вызывает
loadServerModule, которая делаетgetModuleResources({ ssr: { runParams } })— запрос к серверу модуля с флагом SSR.Сервер модуля в
createGetModulesMethodвидит флагssr, вызываетmodule.renderToHtml(moduleState, ssrRunParams), возвращает{ html, scripts, styles, moduleState }.Компонент рендерит:
<style>(default) или<link>приstylesMode: 'link'; атрибутыdata-module-ssr-hrefиdata-parent-app-idдля того чтобы на клиенте рантайм не началл грузить их еще раз;<div>cdata-module-ssr-mount-id;<script type="application/json">сdata-module-ssr-payload.Стили default (module federation) модулей добавляет
AttributeModuleCssPluginпри сборке: обходит граф чанков и записывает css в запись модуля в манифесте. При SSR-запросеcreateGetModulesMethodотдаёт эти стили; для обычных запросов ответ остаётся прежним (css: []).На клиенте
ClientModuleпри первом рендере захватывает snapshot серверной разметки (она уже в DOM) и рендерит её какdangerouslySetInnerHTML— React не трогает стили и payload.Запускает лоадер, обёрнутый в
createEmbeddedModuleFetcher: ищет<script type="application/json">сdata-module-ssr-payload, к сети не обращается. Если payload не найден — падает на fallback-фетчер (исходныйgetModuleResources).Загружает скрипты модуля — через MF runtime (default) или
<script>-теги (compat). Стили сdata-module-ssr-hrefнаследуются и не загружаются повторно.Гидрация: если у модуля есть
hydrate— вызывает его, React гидрирует существующую разметку. Если нет — очищаетinnerHTMLмонтируемого узла и вызываетmount(graceful degradation, warning в dev).Обновление: при изменении
runParamsвызываетmodule.update(target, params, state)— без перемонтирования.Prerelease versions:
@alfalab/scripts-modules@0.0.0-next-20260709074302@alfalab/scripts-server@0.0.0-next-20260709074302arui-scripts@0.0.0-next-20260709074302