Files
jiang13-forum/frontend/renderer/+onRenderClient.tsx
freefire e71c77b5ca feat: Vike 骨架(pages feed+spa / renderer 双端入口 / AppShell 拆分 / serverClient)
vite.config 接入 vike 插件并改产出 dist/client+dist/server;feed 白名单 route 匹配 /、/board/:id、/post/:id(.html);spa catch-all ssr:false;onRenderHtml renderToReadableStream 等 allReady 输出完整 HTML 并带主题/缩放脚本;onRenderClient 沿用样式表等待+冷启动预热+hydrate 回退;getHomeStoreState 作用域 + hydrating 变体入 RequestScope;删除 index.html 与 main.tsx
2026-09-09 01:21:58 +08:00

91 lines
2.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { StrictMode } from 'react';
import type { ComponentType } from 'react';
import { hydrateRoot, createRoot } from 'react-dom/client';
import type { PageContextClient } from 'vike/types';
import { applyTheme, getStoredTheme } from '../src/utils/theme';
import { beginHomeHydrate } from '../src/utils/homeHydrate';
import { reloadForStaleChunk } from '../src/utils/chunkLoad';
import { consumeHomeBoot } from '../src/utils/homeBoot';
import { ensureColdBootReady, isMainLayoutPath } from '../src/utils/prefetchRoute';
export { onRenderClient };
type PageComponent = ComponentType<{ url?: string }>;
let root: ReturnType<typeof createRoot> | null = null;
function waitForStylesheets(): Promise<void> {
const links = Array.from(document.querySelectorAll('link[rel="stylesheet"]'));
if (links.length === 0) return Promise.resolve();
return Promise.all(
links.map(
(node) =>
new Promise<void>((resolve) => {
const link = node as HTMLLinkElement;
try {
if (link.sheet) {
resolve();
return;
}
} catch {
/* cross-origin */
}
const finish = () => resolve();
link.addEventListener('load', finish, { once: true });
link.addEventListener('error', finish, { once: true });
}),
),
).then(() => undefined);
}
async function onRenderClient(pageContext: PageContextClient) {
applyTheme(getStoredTheme());
consumeHomeBoot();
// Vite modulepreload / 动态 import 失败(发版后旧 hashed URL 404)
window.addEventListener('vite:preloadError', (event) => {
event.preventDefault();
reloadForStaleChunk();
});
const el = document.getElementById('root');
if (!el) return;
await waitForStylesheets();
try {
if (document.fonts?.ready) await document.fonts.ready;
} catch {
/* ignore */
}
const path = `${window.location.pathname}${window.location.search}`;
if (isMainLayoutPath(window.location.pathname)) {
await ensureColdBootReady(path);
}
// 有 SSR 正文 → hydrate 接管同一 DOM(首帧 hydrate 同构变体);SPA 壳 → 整树挂载
const hydrating = pageContext.isHydration && el.innerHTML.trim() !== '';
if (hydrating) beginHomeHydrate();
const Page = pageContext.Page as PageComponent;
const app = (
<StrictMode>
<Page />
</StrictMode>
);
if (hydrating) {
try {
hydrateRoot(el, app);
} catch (err) {
console.warn('[j13] hydrateRoot 失败,回退 createRoot', err);
el.innerHTML = '';
if (!root) root = createRoot(el);
root.render(app);
}
} else {
if (!root) root = createRoot(el);
root.render(app);
}
}