React服务端渲染探秘: 5.node做中间层及请求代码优化

1、为何要引入node中间层?

其实任何技术都是与它的应用场景息息相关的。这里咱们反复谈的SSR,其实不到万不得已咱们是用不着它的,SSR所解决的最大的痛点在于SEO,但它同时带来了更昂贵的成本。不只由于服务端渲染须要更加复杂的处理逻辑,还由于同构的过程须要服务端和客户端都执行一遍代码,这虽然对于客户端并无什么大碍,但对于服务端倒是巨大的压力,由于数量庞大的访问量,对于每一次访问都要另外在服务器端执行一遍代码进行计算和编译,大大地消耗了服务器端的性能,成本随之增长。若是访问量足够大的时候,之前不用SSR的时候一台服务器可以承受的压力如今或许要增长到10台才能抗住。痛点在于SEO,但若是实际上对SEO要求并不高的时候,那使用SSR就大可没必要了。前端

那一样地,为何要引入node做为中间层呢?它是处在哪二者的中间?又是解决了什么场景下的问题?node

在不用中间层的先后端分离开发模式下,前端通常直接请求后端的接口。但真实场景下,后端所给的数据格式并非前端想要的,但处于性能缘由或者其余的因素接口格式不能更改,这时候须要在前端作一些额外的数据处理操做。前端来操做数据自己无可厚非,可是当数据量变得庞大起来,那么在客户端就是产生巨大的性能损耗,甚至影响到用户体验。在这个时候,node中间层的概念便应运而生。ios

它最终解决的先后端协做的问题。express

通常的中间层工做流是这样的:前端每次发送请求都是去请求node层的接口,而后node对于相应的前端请求作转发,用node去请求真正的后端接口获取数据,获取后再由node层作对应的数据计算等处理操做,而后返回给前端。这就至关于让node层替前端接管了对数据的操做。json

2、SSR框架中引入中间层

在以前搭建的SSR框架中,服务端和客户端请求利用的是同一套请求后端接口的代码,但这是不科学的。redux

对客户端而言,最好经过node中间层。而对于这个SSR项目而言,node开启的服务器原本就是一个中间层的角色,于是对于服务器端执行数据请求而言,就能够直接请求真正的后端接口啦。axios

//actions.js
//参数server表示当前请求是否发生在node服务端
const getUrl = (server) => {
    return server ? 'xxxx(后端接口地址)' : '/api/sanyuan.json(node接口)';
}
//这个server参数是Home组件里面传过来的,
//在componentDidMount中调用这个action时传入false,
//在loadData函数中调用时传入true, 这里就不贴组件代码了
export const getHomeList = (server) => {
  return dispatch => {
    return axios.get(getUrl(server))
      .then((res) => {
        const list = res.data.data;
        dispatch(changeList(list))
      })
  }
}
复制代码

在server/index.js应拿到前端的请求作转发,这里是直接用proxy形式来作,也能够用node单独向后端发送一次HTTP请求。后端

//增长以下代码
import proxy from 'express-http-proxy';
//至关于拦截到了前端请求地址中的/api部分,而后换成另外一个地址
app.use('/api', proxy('http://xxxxxx(服务端地址)', {
  proxyReqPathResolver: function(req) {
    return '/api'+req.url;
  }
}));
复制代码

3、请求代码优化

其实请求的代码仍是有优化的余地的,仔细想一想,上面的server参数实际上是不用传递的。api

如今咱们利用axios的instance和thunk里面的withExtraArgument来作一些封装。服务器

//新建server/request.js
import axios from 'axios'

const instance = axios.create({
  baseURL: 'http://xxxxxx(服务端地址)'
})

export default instance


//新建client/request.js
import axios from 'axios'

const instance = axios.create({
  //即当前路径的node服务
  baseURL: '/'
})

export default instance
复制代码

而后对全局下store的代码作一个微调:

import {createStore, applyMiddleware, combineReducers} from 'redux';
import thunk from 'redux-thunk';
import { reducer as homeReducer } from '../containers/Home/store';
import clientAxios from '../client/request';
import serverAxios from '../server/request';

const reducer = combineReducers({
  home: homeReducer
})

export const getStore = () => {
  //让thunk中间件带上serverAxios
  return createStore(reducer, applyMiddleware(thunk.withExtraArgument(serverAxios)));
}
export const getClientStore = () => {
  const defaultState = window.context ? window.context.state : {};
   //让thunk中间件带上clientAxios
  return createStore(reducer, defaultState, applyMiddleware(thunk.withExtraArgument(clientAxios)));
}

复制代码

如今Home组件中请求数据的action无需传参,actions.js中的请求代码以下:

export const getHomeList = () => {
  //返回函数中的默认第三个参数是withExtraArgument传进来的axios实例
  return (dispatch, getState, axiosInstance) => {
    return axiosInstance.get('/api/sanyuan.json')
      .then((res) => {
        const list = res.data.data;
        console.log(res)
        dispatch(changeList(list))
      })
  }
}
复制代码

至此,代码优化就作的差很少了,这种代码封装的技巧其实能够用在其余的项目当中,其实仍是比较优雅的。

相关文章
相关标签/搜索