Skip to content

Commit ead8c7b

Browse files
committed
React SSR + fetch data by server
1 parent 22084f2 commit ead8c7b

File tree

8 files changed

+72
-12
lines changed

8 files changed

+72
-12
lines changed

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
Try React SSR
2+
3+
how to run:
4+
5+
```shell
6+
# terminal 1
7+
npm install
8+
npx webpack --watch
9+
10+
# terminal 2
11+
npm start
12+
```

dist/bundle.js

Lines changed: 13 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

mockData.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export const fetchMockData = () => {
2+
return new Promise((resolve) => {
3+
// 模拟网络延迟
4+
setTimeout(() => {
5+
// 返回模拟数据
6+
resolve({ message: 'This is mock data from the API.' });
7+
}, 1000); // 1秒延迟
8+
});
9+
};

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
"@babel/core": "^7.25.2",
1616
"@babel/preset-env": "^7.25.3",
1717
"@babel/preset-react": "^7.24.7",
18-
"@remix-run/router": "^1.19.1",
1918
"babel-loader": "^9.1.3",
2019
"express": "^4.19.2",
2120
"react": "^18.3.1",

server.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,19 @@ import React from 'react';
55
import ReactDOMServer from 'react-dom/server';
66
import { StaticRouter } from 'react-router-dom/server';
77
import App from './src/App';
8+
import { fetchMockData } from './mockData'; // 导入 mock 数据函数
89

910
const app = express();
1011

1112
app.use(express.static(path.resolve(__dirname, 'dist')));
1213

13-
app.get('*', (req, res) => {
14+
app.get('*', async (req, res) => {
15+
const mockData = await fetchMockData(); // 使用 mock 数据函数
16+
1417
const context = {};
15-
1618
const appHtml = ReactDOMServer.renderToString(
1719
<StaticRouter location={req.url} context={context}>
18-
<App />
20+
<App serverData={mockData} />
1921
</StaticRouter>
2022
);
2123

@@ -40,6 +42,9 @@ app.get('*', (req, res) => {
4042
</head>
4143
<body>
4244
<div id="root">${appHtml}</div>
45+
<script>
46+
window.__INITIAL_DATA__ = ${JSON.stringify(mockData)};
47+
</script>
4348
<script src="/bundle.js"></script>
4449
</body>
4550
</html>

src/App.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { Routes, Route, Link } from 'react-router-dom';
33
import Home from './Home';
44
import Blog from './Blog';
55

6-
function App() {
6+
function App({serverData}) {
77
return (
88
<div>
99
<nav>
@@ -12,7 +12,7 @@ function App() {
1212
<Link to="/blog/1">Blog 1</Link>
1313
</nav>
1414
<Routes>
15-
<Route path="/" element={<Home />} />
15+
<Route path="/" element={<Home serverData={serverData} />} />
1616
<Route path="/blog/:id" element={<Blog />} />
1717
</Routes>
1818
</div>

src/Home.js

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,32 @@
1-
import React, { useState } from 'react';
1+
import React, { useState, useEffect } from 'react';
2+
import { fetchMockData } from '../mockData'; // 导入 mock 数据函数
23

3-
function Home() {
4+
function Home({serverData}) {
45
const [clicked, setClicked] = useState(false);
6+
const [data, setData] = useState(serverData || null);
7+
8+
useEffect(() => {
9+
console.log("serverData", serverData)
10+
if (!serverData || !serverData.message) {
11+
// 为什么这里也需要一次fetch?
12+
// 目的是为了防止服务器SSR获取出错(如缓存击穿就放弃获取)的异常情况,客户端进行一个补偿容错机制
13+
// SSR服务器往往是从内网获取数据,
14+
// 而CSR是从公网获取数据
15+
// 两者走的网络甚至服务器都有可能不一样。
16+
// 根据具体的业务逻辑可以设计让系统更加强健,如果服务器获取失败,客户端可以获取一个别的版本的数据
17+
fetchMockData()
18+
.then(data => {
19+
data.message = data.message + "(From CSR)";
20+
setData(data);
21+
})
22+
.catch(err => console.error('Mock fetch failed:', err));
23+
}
24+
}, [serverData]);
525

626
return (
727
<div>
828
<h1>Home Page</h1>
29+
<p>Data: {JSON.stringify(data)}</p>
930
<button onClick={() => setClicked(!clicked)}>
1031
{clicked ? 'Clicked!' : 'Click me'}
1132
</button>

src/index.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,12 @@ import { BrowserRouter } from 'react-router-dom';
44
import App from './App';
55

66
const root = ReactDOM.createRoot(document.getElementById('root'));
7+
8+
const data = window.__INITIAL_DATA__; // 从 SSR 注入的数据
9+
console.log("window.__INITIAL_DATA__", window.__INITIAL_DATA__)
10+
711
root.render(
812
<BrowserRouter>
9-
<App />
13+
<App serverData={data}/>
1014
</BrowserRouter>
1115
);

0 commit comments

Comments
 (0)