React服务端渲染原理分析|基础实现
服务器端渲染中的路由
首先浏览器向服务器发送请求,服务器返回一个空的html,浏览器再请求js,加载到js后会执行react代码,react代码接管页面执行流程,这个时候可以根据浏览器的地址展示页面内容。
当我们做重构的时候我们需要让路由代码在浏览器和服务端分别执行一次,浏览器执行的流程和原本一模一样没有任何区别但是服务器端有一些区别,这里要使用StaticRouter组件替代浏览器的browserRouter。
npm install react-router-dom --save
创建src/Routes.js配置路由。
import React from 'react';
import { Route } from 'react-router-dom';
import Home from './components/Home';
export default (
<div>
<Route path="/" exact component={Home}></Route>
</div>
);
在做同构的时候我们要让路由在服务器跑一遍也要在客户端跑一遍。
src/client/index.js, 这里使用BrowserRouter包裹住之前定义的Routes,Home删掉就可以了,因为Routes中已经引入了Home。
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import Routes from '../Routes';
const App = () => {
return (
<BrowserRouter>
{Routes}
</BrowserRouter>
)
}
ReactDOM.hydrate(<App />, document.getElementById('root'));
src/server/index.js,同样使用StaticRouter来渲染Routes。Home在Routes中已经引入了这里就不需要引入了。context是StaticRouter做数据传递的,这里先写一个空对象。
StaticRouter是不知道请求路径是什么的,因为他运行在服务器端,所以这是他不如BrowserRouter的地方,他需要在请求体重获取到路径传递给他, 这里我们就需要将content写在请求里面。将location的值赋为req.path。
import express from 'express';
import React from 'react';
import { renderToString } from 'react-dom/server';
import { StaticRouter } from 'react-router-dom';
import Routes from '../Routes';
const app = express();
app.use(express.static('public'));
app.get('*', function(req, res) {
const content = renderToString((
<StaticRouter location={req.path} context={{}}>
<Routes />
</StaticRouter>
));
res.send(`
<html>
<body>
<div id="root">${content}</div>
<script src="/index.js"></script>
</body>
</html>
`);
})
var server = app.listen(3000);
我们这里增加一个页面,实现多页面的路由跳转。当用户访问login路径的时候我们返回一个Login组件。
src/Routes.js
import React from 'react';
import { Route } from 'react-router-dom';
import Home from './components/Home';
import Login from './components/Login';
export default (
<div>
<Route path="/" exact component={Home}></Route>
<Route path="/login" exact component={Login}></Route>
</div>
);
src/components/Login/index.js
import React from 'react';
const Login = () => {
return <div>Login</div>
}
export default Login;
这个时候页面就可以打开了,并且login路由可以加载出来。我们来整理一下代码,新建一个utils文件将通用方法抽离出来。
src/server/utils.js
import React from 'react';
import { renderToString } from 'react-dom/server';
import { StaticRouter } from 'react-router-dom';
import Routes from '../Routes';
export const render = (req) => {
const content = renderToString((
<StaticRouter location={req.path} context={{}}>
<Routes />
</StaticRouter>
));
return `
<html>
<body>
<div id="root">${content}</div>
<script src="/index.js"></script>
</body>
</html>
`;
}
整理一下src/server/index.js,使用render函数。
import express from 'express';
import { render } from './utils';
const app = express();
app.use(express.static('public'));
app.get('*', function(req, res) {
res.send(render(req));
})
var server = app.listen(3000);
这样代码就整理好了,我们接着使用Link标签串联起整个路由的工作流程。
我们创建一个公共组件src/components/Header/index.js
import React from 'react';
const Header = () => {
return <div>header</div>
}
export default Header;
我们再src/components/Home/index.js组件中引入Header组件。
import React from 'react';
import Header from '../Header';
const Home = () => {
return <div>
<Header>
Home
<button onClick={() => { alert('click1'); }>按钮</button>
</div>
}
export default Home;
src/components/Login/index.js
import React from 'react';
import Header from '../Header';
const Login = () => {
return <div><Header />Login</div>
}
export default Login;
页面可以正常执行,接着我们在Header中引入Link, 并且使用他跳转至Home和Login。
src/components/Header/index.js
import React from 'react';
import { Link } from 'react-router-dom';
const Header = () => {
return <div>
<Link to="/">Home</Link>
<br />
<Link to="/login">Login</Link>
</div>
}
export default Header;
当我们在做页面同构的时候,服务器端渲染只放生在我们第一次进入页面的时候,后面使用Link的跳转都是浏览器端的跳转,不会再去加载页面的资源文件。
所以服务器端渲染不是每个页面都做服务器端渲染,而是只访问的第一个页面具有服务端渲染的特性,其他的页面仍旧是React的路由机制, 这是我们要注意的。
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!