基础
- renderToString:是用于服务器端渲染(SSR)React 应用程序的代码片段。它是 React 库中的一个模块,用于将 React 组件渲染为字符串,以便在服务器上生成 HTML,从而实现服务器端渲染。
- hydrate:是用于在客户端进行混合的 React 库模块。在使用服务器端渲染(SSR)生成的 HTML 页面中,通过
hydrate
函数可以将客户端渲染(CSR)所需的事件处理程序和状态绑定到已经存在的 DOM 上,从而实现服务器端渲染与客户端交互的无缝切换。
实践
app.js
import express from 'express'
import path from 'path'
import template from './src/template'
import ssr from './src/server'
import data from './assets/data.json'
const app = express()
app.use('/assets', express.static(path.resolve(__dirname, 'assets')))
app.use('/media', express.static(path.resolve(__dirname, 'media')))
app.disable('x-powered-by')
app.listen(process.env.PORT || 3000)
let initialState = {
isFetching: false,
apps: data
}
app.get('/', (req, res) => {
const { preloadedState, content} = ssr(initialState)
const response = template("Server Rendered Page", preloadedState, content)
res.setHeader('Cache-Control', 'assets, max-age=604800')
res.send(response);
});
app.get('/client', (req, res) => {
let response = template('Client Side Rendered page')
res.setHeader('Cache-Control', 'assets, max-age=604800')
res.send(response)
});
server.js
import React from 'react'
import { renderToString } from 'react-dom/server'
import { Provider } from 'react-redux'
import configureStore from './redux/configureStore'
import App from './components/app'
module.exports = function render(initialState) {
const store = configureStore(initialState)
let content = renderToString(
<Provider store={store} >
<App />
</Provider>
);
const preloadedState = store.getState()
return {content, preloadedState};
}
template.js
export default function template(title, initialState = {}, content = "") {
let scripts = ''
if (content) {
scripts = ` <script>
window.__STATE__ = ${JSON.stringify(initialState)}
</script>
<script src="assets/client.js"></script>
`
} else {
scripts = ` <script src="assets/bundle.js"> </script> `
}
let page = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title> ${title} </title>
<link rel="stylesheet" href="assets/style.css">
</head>
<body>
<div class="content">
<div id="app" class="wrap-inner">
${content}
</div>
</div>
${scripts}
</body>
`
return page
}
client.js
import React from 'react'
import {hydrate} from 'react-dom'
import {Provider} from 'react-redux'
import configureStore from './redux/configureStore'
import App from './components/app'
const state = window.__STATE__;
delete window.__STATE__;
const store = configureStore(state)
hydrate(
<Provider store={store} >
<App />
</Provider>,
document.querySelector('#app')
)