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))
      })
  }
}
複製代碼

至此,代碼優化就作的差很少了,這種代碼封裝的技巧其實能夠用在其餘的項目當中,其實仍是比較優雅的。

相關文章
相關標籤/搜索