路由机制

  在客户端执行路由我们使用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这个目录,于是网页一直转

 

posted on 2018-12-11 06:31  苏荷酒吧  阅读(135)  评论(0)    收藏  举报