import type { ComponentType, ReactElement } from 'react'; import { renderToReadableStream } from 'react-dom/server'; import { escapeInject, dangerouslySkipEscape } from 'vike/server'; import type { PageContextServer } from 'vike/types'; import { createHomeStore } from '../src/store/homeStore'; import { createRequestScope } from '../src/ssr/scopeTypes'; import { runWithScope } from '../src/ssr/scope'; import { seedFeedData, type FeedData } from '../src/ssr/feedSeed'; import { renderHeadTags } from '../src/ssr/head'; import { installServerDom } from '../src/ssr/serverDom'; import { readThemeFromCookie } from '../src/utils/theme'; import { createSsrApp } from '../src/App'; // 服务端 DOM polyfill:内容处理依赖 DOMParser/document,须在渲染前安装 installServerDom(); export { onRenderHtml }; type PageComponent = ComponentType<{ url?: string }>; /** 等 lazy 页面全部渲染完成(allReady)后缓冲为完整 HTML,首字节语义与旧 Go 文档级 SSR 一致 */ async function renderPageToString(element: ReactElement): Promise { const stream = await renderToReadableStream(element, { onError(err) { console.error('[j13] SSR 渲染错误:', err); }, }); await stream.allReady; return new Response(stream).text(); } async function onRenderHtml(pageContext: PageContextServer) { const Page = pageContext.Page as PageComponent | undefined; const data = (pageContext as { data?: FeedData }).data; let pageHtml = ''; if (Page) { // SSR feed 页:per-request 作用域 + hydrate 同构变体;渲染失败降级空壳(客户端自行接管),不 500 const scope = createRequestScope(createHomeStore()); scope.hydrating = true; const { pathname, searchOriginal } = pageContext.urlParsed; const url = pathname + (searchOriginal ?? ''); try { pageHtml = await runWithScope(scope, async () => { // 渲染前把 +data 取回的数据 seed 进请求作用域(缓存层按 scope 隔离) seedFeedData(data); // 官方 SSR 路径:await 首导航后 StaticRouterProvider 同步渲染(避免 RouterProvider suspend // 导致内容落入