update
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { hydrateRoot } from 'react-dom/client';
|
||||
import { hydrateRoot, createRoot } from 'react-dom/client';
|
||||
import App from './pages/a/ClientApp';
|
||||
// import AServer from './pages/a/server/index.tsx';
|
||||
|
||||
@@ -14,5 +14,11 @@ declare global {
|
||||
|
||||
if (typeof document !== 'undefined') {
|
||||
// const data = window.__SERVER_DATA__ ?? { version: '' };
|
||||
hydrateRoot(document.getElementById('root')!, <App />);
|
||||
// hydrateRoot(document.getElementById('root')!, <App />);
|
||||
|
||||
// hydrateRoot(document.getElementById('root')!, <App />);
|
||||
// console.log('Hydration complete');
|
||||
|
||||
const root = createRoot(document.getElementById('root')!);
|
||||
root.render(<App />);
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import A from './pages/a/List';
|
||||
Bun.serve({
|
||||
port: 3000,
|
||||
async fetch(req) {
|
||||
const stream = await renderToReadableStream(<A version=''/>, {
|
||||
const stream = await renderToReadableStream(<A version='' data={{}} />, {
|
||||
bootstrapScripts: [],
|
||||
});
|
||||
|
||||
|
||||
@@ -1,19 +1,12 @@
|
||||
'use client';
|
||||
import List from './List.tsx';
|
||||
|
||||
// Client Component - 用于 hydration,结构需要和 ServerApp 一致
|
||||
declare global {
|
||||
interface Window {
|
||||
__SERVER_DATA__?: { version: string };
|
||||
}
|
||||
}
|
||||
|
||||
export default function ClientApp() {
|
||||
const version = typeof window !== 'undefined' && window.__SERVER_DATA__?.version
|
||||
? window.__SERVER_DATA__.version
|
||||
: 'loading';
|
||||
|
||||
return <List version={version} />;
|
||||
const data = typeof window !== 'undefined' && window.__SERVER_DATA__ ? window.__SERVER_DATA__ : { version: 'loading' };
|
||||
return <List
|
||||
version={data.version}
|
||||
data={data}
|
||||
/>;
|
||||
}
|
||||
|
||||
// 'use client';
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
"use client";
|
||||
import { useEffect } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
export default function List({ version }: { version: string }) {
|
||||
const Item = () => {
|
||||
return <div>Item</div>
|
||||
}
|
||||
export default function List({ version, data = {} }: { version: string, data: any }) {
|
||||
useEffect(() => {
|
||||
console.log('useEffect in List123');
|
||||
console.log('Received data in List:', data);
|
||||
}, []);
|
||||
// <div>Timestamp: {timestamp ? new Date(timestamp).toLocaleTimeString('en-US') : 'N/A'}</div>
|
||||
const tm = <div>Timestamp: {data.timestamp ? new Date(data.timestamp).toLocaleTimeString('en-US') : 'N/A'}</div>
|
||||
return (
|
||||
<div>
|
||||
<div>sdf3</div>
|
||||
<h1>List - Version {version}</h1>
|
||||
<div>sdf32323232</div>
|
||||
{tm}
|
||||
{version === '2.0.0' && <Item />}
|
||||
{/* <h1>List - Version {version}</h1> */}
|
||||
{/* <h1>List - Version2 {version + 1}</h1> */}
|
||||
<div style={{
|
||||
width: 200
|
||||
}}>Primary Button</div>
|
||||
|
||||
@@ -8,7 +8,8 @@ type ServerData = {
|
||||
async function fetchServerData() {
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
return {
|
||||
version: '3.0.0',
|
||||
// version: '3.0.0' + Math.floor(Math.random() * 100),
|
||||
version: '2.0.0',
|
||||
timestamp: Date.now(),
|
||||
innerHtml: '<div>Inner HTML Content</div>'
|
||||
};
|
||||
@@ -27,6 +28,6 @@ export const getData = fetchServerData; // 导出数据获取函数,供 server
|
||||
// Server Component - 通过 props 接收数据,不再自己 fetch
|
||||
export default async function ServerApp(props: { data: ServerData }) {
|
||||
return <>
|
||||
<List version={props.data.version} />
|
||||
<List version={props.data.version} data={props.data} />
|
||||
</>
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import Main, { createServerDataScript, getData } from './pages/a/main.tsx';
|
||||
import http from 'http';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
import fg from 'fast-glob'
|
||||
const PORT = 3000;
|
||||
const distDir = path.join(process.cwd(), 'dist');
|
||||
const indexHtmlPath = path.join(process.cwd(), 'dist/index.html');
|
||||
@@ -19,10 +19,24 @@ const mimeTypes: Record<string, string> = {
|
||||
'.ico': 'image/x-icon',
|
||||
};
|
||||
|
||||
const indexList = fg.globSync('index-*.js', {
|
||||
cwd: process.cwd() + '/dist',
|
||||
})
|
||||
const indexPath = indexList.map(file => {
|
||||
const basename = path.basename(file, '.js');
|
||||
const [_, hash] = basename.split('-');
|
||||
console.log('Found index file:', `http://localhost:${PORT}/${basename}`);
|
||||
return { file, hash, basename };
|
||||
});
|
||||
|
||||
|
||||
async function ssrRender(res: http.ServerResponse, mode: 'server' | 'client' = 'client') {
|
||||
async function ssrRender(res: http.ServerResponse, opts: {
|
||||
mode: 'server' | 'client',
|
||||
urlPath: string,
|
||||
filename?: string
|
||||
}) {
|
||||
let template: string;
|
||||
const mode = opts.mode || 'client';
|
||||
try {
|
||||
template = fs.readFileSync(indexHtmlPath, 'utf-8');
|
||||
} catch {
|
||||
@@ -30,7 +44,7 @@ async function ssrRender(res: http.ServerResponse, mode: 'server' | 'client' = '
|
||||
res.end('dist/index.html not found');
|
||||
return;
|
||||
}
|
||||
if (mode === 'client' ) {
|
||||
if (mode === 'client') {
|
||||
res.writeHead(200, { 'Content-Type': 'text/html' });
|
||||
res.end(template);
|
||||
return;
|
||||
@@ -38,19 +52,21 @@ async function ssrRender(res: http.ServerResponse, mode: 'server' | 'client' = '
|
||||
|
||||
try {
|
||||
const serverData = await getData()
|
||||
const filename = opts.filename;
|
||||
const bootstrapScripts = [filename]
|
||||
const scriptTag = bootstrapScripts.map(src => `<script type='module' src="${src}"/></script>`).join('');
|
||||
const stream = await renderToReadableStream(<Main data={serverData} />, {
|
||||
bootstrapScripts: []
|
||||
});
|
||||
|
||||
let renderedHtml = '';
|
||||
for await (const chunk of stream) {
|
||||
renderedHtml += new TextDecoder().decode(chunk);
|
||||
}
|
||||
|
||||
// console.log('Rendered HTML:', renderedHtml);
|
||||
console.log('Rendered HTML:', renderedHtml);
|
||||
const html = template
|
||||
.replace('<div id="root"></div>', `<div id="root">${renderedHtml}</div>`)
|
||||
.replace('</head>', `${createServerDataScript(serverData)}</head>`);
|
||||
.replace('</head>', `${createServerDataScript(serverData)}</head>`)
|
||||
.replace('</body>', `${scriptTag}</body>`);
|
||||
res.writeHead(200, { 'Content-Type': 'text/html' });
|
||||
res.end(html);
|
||||
} catch (err) {
|
||||
@@ -64,12 +80,12 @@ http.createServer((req, res) => {
|
||||
const urlPath = req.url?.split('?')[0] || '/';
|
||||
|
||||
if (urlPath === '/') {
|
||||
ssrRender(res, 'client');
|
||||
ssrRender(res, { mode: 'client', urlPath: '' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (urlPath === '/a') {
|
||||
ssrRender(res, 'server');
|
||||
const find = indexPath.find((item) => urlPath === `/${item.basename}`);
|
||||
if (find) {
|
||||
ssrRender(res, { mode: 'server', urlPath, filename: './' + find.file });
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -85,7 +101,7 @@ http.createServer((req, res) => {
|
||||
} catch {
|
||||
}
|
||||
|
||||
ssrRender(res);
|
||||
// ssrRender(res, { mode: 'client', urlPath: '/a' });
|
||||
}).listen(PORT, () => {
|
||||
console.log(`Server running on http://localhost:${PORT}`);
|
||||
});
|
||||
Reference in New Issue
Block a user