路由机制
在客户端执行路由我们使用react-router
而且在服务器运行路由使用static-router
安装
npm install react-router-dom --save
在根目录下创建routes.js文件,引入配置Home页面路由
export default ( <div> <Route path='/' component={Home} /> </div> )
接下来修改client/index.js,客户端使用BrowserRouter
const App = () => ( <BrowserRouter> {Routes} </BrowserRouter> ) ReactDOM.hydrate(<App/>, document.getElementById('root'))
修改server/index.js服务器端使用StaticRouter
var app = express() app.use(express.static('public')) app.get('/', function (req, res) { const content = renderToString(( <StaticRouter location={req.path} context={{}}> {routers} </StaticRouter> )) res.send( `<html> <head> <title>hello</title> </head> <body> <div id="root">${content}</div> <script src="/index.js"></script> </body> </html>` ) }) var server = app.listen(3000)
到此我们路由配置成功可以访问页面
多页面路由跳转
我们在src/pages/login/index.js创建一个LoginComponent组件
然后配置routes.js路由此刻打包启动服务器是不执行发现跳转不到/login
我们此刻要修改src/server/index.js文件
把app.get('/')修改成app.get('*')
Link 标签
react同构只发生在第一次页面加载的时候,如果Link跳转页面不是服务器端渲染,也就是客户端接管页面。服务器渲染只发生在首屏的时候
Redux
首先安装
npm i redux react-redux redux-thunk --save
跟之前一样在src/store创建reducer.js,index.js,然后服务器端和客户端分别引用
不同之处是 store/index.js
const getStore = () => ({ reducer, applyMiddleware(thunk) })
服务器和客户端分别执行 这样保证每个用户都是独立的store
<Provider store={getStore()}>
Ajax请求
首先服务器发送请求不会执行componentDODMount所以store为空
然后客户端代码运行,此刻会执行componentDidMounted,store中有数据然后组件render渲染出内容也就是
也就是componentDidMounted是客户端渲染,页面源码是看不到数据
使用loadData方法
在Home组件创建loadData方法
Home.loadData = (store) => { return store.dispatch(actions.getHomeListAction()) }
修改路由配置 ,添加loadData
export default [ { path:'/', component:Home, exact:true, loadData: Home.loadData, key:'home' } ]
然后我们先修改客户端代码,因为现在路由是数组
{ routes.map(route=>( <Route key={route.key} {...route} /> )) }
然后修改服务器端代码首先跟上面一样修改路由配置
安装react-router-config
首先我们要使用matchRoutes 根据请求地址可以获得Route的信息
const matchedRoutes = matchRoutes(routes, req.path) const promises = [] matchedRoutes.forEach(item => { if (item.route.loadData) { promises.push(item.route.loadData(store)) } })
因为数据ajax请求是异步请求所以使用Promise.all 然后服务器渲染页面,不然页面加载不了数据
Promise.all(promises).then(()=>{
console.log(store)
res.send(render(store, routes, req))
})
到此页面的源码中能看到服务器ajax请求的数据
但是还有个问题当刷新页面时,数据会出现闪屏,这是因为服务器端渲染一个React,客户端也渲染一次React
所以我们需要数据脱水和数据注水
在服务器端我们修改配置在html中加入,服务器端渲染会把window.context注入到页面中这是数据的注水过程
<script> window.context = { state: ${JSON.stringify(store.getState())} } </script>
然后修改store/index.js为客户端,客户端使用 服务器window.context的数据,这就是数据的脱水过程
const getClientStore = () => { const defaultState = window.context.state return createStore(reducer, applyMiddleware(thunk) ) }
这两部配置 再次刷新页面就不会出现闪屏
使用proxy,让中间层获取数据
安装express-http-proxy
首先修改我们axios代码,之前我们使用http://localhost:8080访问本地启动的服务器请求数据
这时候我们使用proxy中间层
在server/index.js文件下把之前服务器请求代码先注释掉,添加proxy,具体代码如下
import proxy from 'express-http-proxy' app.use('/api', proxy('http://192.168.1.105:8080', { proxyReqPathResolver: function(req) { return '/api' + req.url } }))
用户访问/api路径下,就会代理到http://192.168.1.105:8080 /api/8080
然后把服务器请求代码注释去掉,会发现网页一直在在转
服务器 http://192.168.1.105:8080/api/home
浏览器运行 /api/home = http://localhost:3000/api/home
服务器运行/api/home = 服务器跟目录下的 /api/home
我们看出server目录下没有api这个目录,于是网页一直转
浙公网安备 33010602011771号