使用Node搭建reactSSR服务端渲染架构是一个相对复杂的过程,需要以下步骤:
1. 创建基础项目
我们可以使用脚手架工具create-react-app创建一个基础的React项目。
npx create-react-app my-app --template typescript
之后需要安装一些依赖包,包括react、react-dom、react-router-dom以及其他一些常用的包。并且需要安装一些开发依赖包,如babel-plugin-import、@types/react等。
2. 配置服务端渲染环境
我们需要在项目根目录下创建一个server.js文件,并且在其中进行服务器的配置。在配置中需要引用react、react-dom、express等核心模块,同时还需要使用babel进行代码编译等。
import express from 'express';
import React from 'react';
import ReactDOMServer from 'react-dom/server';
import App from './App'; // 项目根组件
const app = express();
// 配置路由,注意路由的“*”表示匹配所有路由
app.get('*', (req, res) => {
const appString = ReactDOMServer.renderToString(<App />);
res.send(`<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>React SSR example</title>
</head>
<body>
<div id="root">${appString}</div>
<script src="/static/js/bundle.js"></script>
</body>
</html>
`);
});
app.listen(3000, () => {
console.log('Server is listening on port 3000');
});
需要注意的是,在用ReactDOMServer将React组件渲染成HTML字符串时,需要在服务器端进行组件的渲染。因此需要使用ReactDOMServer的renderToString函数,渲染完成后将渲染出的内容以字符串形式返回给客户端进行处理。
3. 配置Webpack打包
在服务端渲染架构中,我们需要将所有客户端代码打包成一个单独的文件,以供服务端渲染时使用。这个打包过程可以通过Webpack进行。
我们需要修改package.json文件中的启动脚本,以便在打包文件时可以一并打包服务端渲染代码:
"scripts": {
"start": "npm run build && node server.js",
"build": "react-scripts build && node build-server.js"
},
在根目录下创建一个build-server.js文件,并在其中进行Webpack打包的相关配置。
const path = require('path');
const webpack = require('webpack');
const webpackClientConfig = require('./node_modules/react-scripts/config/webpack.config.js')('development');
const webpackServerConfig = {
...webpackClientConfig,
entry: {
server: path.resolve(__dirname, './server.js')
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, './build')
},
target: 'node'
};
webpack([webpackClientConfig, webpackServerConfig]).run((err, stats) => {
if (err) {
console.error(err);
} else {
const info = stats.toJson();
if (stats.hasErrors()) {
console.error(info.errors);
process.exit(1);
} else {
console.log(info);
}
}
});
在此文件中,我们首先获取了默认的Webpack配置(webpackClientConfig),并在此基础上针对服务端渲染进行了修改,包括:
- 修改entry为server.js文件路径;
- 修改output为输出到build文件夹下,文件名为[name].js,即使用entry的名字作为输出文件名;
- 修改target为node,以保证打包后的JS文件可以在Node.js环境下执行。
4. 开始使用服务端渲染
完成以上的配置后,我们就可以使用服务端渲染来处理React组件的渲染了。
首先,在页面中引用我们打包生成的client.js文件:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>React SSR example</title>
</head>
<body>
<div id="root"></div>
<script src="/static/js/bundle.js"></script>
<script src="/static/js/client.js"></script>
</body>
</html>
在client.js中,我们需要判断当前环境是否支持服务端渲染,如果支持,就使用ReactDOM.hydrate进行渲染,否则就使用ReactDOM.render进行客户端渲染。
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App'; // 项目根组件
const rootElement = document.getElementById('root');
if (rootElement.hasChildNodes()) {
ReactDOM.hydrate(<App />, rootElement);
} else {
ReactDOM.render(<App />, rootElement);
}
示例
示例一:使用服务器数据渲染组件
我们可以使用fetch等方式从服务器获取数据,并将数据传入React组件中进行渲染。
import React from 'react';
interface UserInfo {
id: number;
name: string;
age: number;
}
interface Props {
userId: number;
}
interface State {
user: UserInfo | null;
loading: boolean;
}
export default class User extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
user: null,
loading: true
};
}
componentDidMount() {
const id = this.props.userId;
fetch(`/api/user?id=${id}`)
.then(response => response.json())
.then(data => {
this.setState({ user: data, loading: false });
});
}
render() {
if (this.state.loading) {
return (
<div>Loading...</div>
);
} else {
return (
<div>
User Info:
<ul>
<li>ID: {this.state.user?.id}</li>
<li>Name: {this.state.user?.name}</li>
<li>Age: {this.state.user?.age}</li>
</ul>
</div>
);
}
}
}
在服务端,我们需要使用axios等方式获取数据,并把数据渲染到HTML字符串中。
import axios from 'axios';
import React from 'react';
import ReactDOMServer from 'react-dom/server';
import User from './User'; // 我们刚才创建的User组件
const app = express();
app.get('/user/:id', async (req, res) => {
const response = await axios.get(`/api/user?id=${req.params.id}`);
const user = response.data;
const appString = ReactDOMServer.renderToString(<User userId={user.id} />);
res.send(`<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>React SSR example</title>
</head>
<body>
${appString}
</body>
</html>
`);
});
在以上代码中,我们首先使用axios进行数据请求,获取到用户信息后,将其传入User组件中,并用ReactDOMServer.renderToString将组件渲染成HTML字符串。最后将HTML字符串返回给客户端。
示例二:使用react-router进行页面路由
使用react-router进行页面路由非常方便,但在服务端渲染中需要进行特殊处理,以保证客户端在进行路由切换时可以正确地进行服务端渲染。
import React from 'react';
import { Route, Switch } from 'react-router-dom';
import Home from './pages/Home';
import About from './pages/About';
import Contact from './pages/Contact';
export default class App extends React.Component {
render() {
return (
<Switch>
<Route exact path="/" component={Home} />
<Route exact path="/about" component={About} />
<Route exact path="/contact" component={Contact} />
</Switch>
);
}
}
对于上述代码,我们需要在服务端根据浏览器请求的URL来选择渲染哪个组件。同时还需要根据客户端路由的路径,来正确地选择渲染哪个页面,以保证服务端渲染和客户端渲染时输出的DOM树相同。
import React from 'react';
import ReactDOMServer from 'react-dom/server';
import { StaticRouter } from 'react-router-dom';
import App from './App'; // 我们刚才创建的根组件
app.get('*', (req, res) => {
const context = {};
const appString = ReactDOMServer.renderToString(
<StaticRouter location={req.url} context={context}>
<App />
</StaticRouter>
);
if (context.url) {
res.redirect(301, context.url);
} else {
res.send(`<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>React SSR example</title>
</head>
<body>
<div id="root">${appString}</div>
<script src="/static/js/bundle.js"></script>
<script src="/static/js/client.js"></script>
</body>
</html>
`);
}
});
在上述代码中,我们使用了StaticRouter组件来渲染路由,其中的location和context参数分别表示当前浏览器请求的URL、和服务端渲染上下文对象。其中上下文对象主要用于重定向或修改HTTP响应码等操作。
最后,如果context.url存在,则表示需要重定向到另一个URL,可通过res.redirect函数实现。否则,如果context.url不存在,则表示当前路径可以正常渲染,使用ReactDOMServer.renderToString将根组件渲染成HTML字符串后返回给客户端。
以上就是Node搭建reactSSR服务端渲染架构的完整攻略。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:使用Node搭建reactSSR服务端渲染架构 - Python技术站