Next.js腳手架進階 — 封裝fetch && 增加中介軟體

luffyZh發表於2018-12-10

Next.js腳手架進階系列

封裝fetch

首先先來說一下為什麼要用fetch而不是axios吧,主要有以下兩點:

  • 第一,我在另一個腳手架express-react-scaffold裡使用的就是axios,秉著學習新東西的想法,想自己封裝一下fetch。
  • 第二,個人覺得fetch的功能更為強大,因為fetch是原生支援的API,更加的底層,所以可擴充套件性更好,經過封裝擴充套件過後的fetch應該是很強大的,由於看了很多成熟腳手架用的都是fetch,我覺得這個觀點還可以接受吧。(P.S.個人觀點,不喜勿噴)

fetch的response

為什麼要單獨說一下fetch的response呢,個人認為這一點既是fetch的缺點又是fetch的優點,我們先來對比一下正常的請求fetch和axios的區別

// fetch
fetch(url)
    .then(res => { // 第一層res
        res.json() // 需要json過後才是我們想要的資料
            .then(r => { // 第二層res        
               console.log(r) // 獲取到最後資料       
        })
});

// axios
axios(url)
    .then(res=>{ // 第一層res
        console.log(res); // 獲取到我們想要的資料
});

複製程式碼

如上面所示,我們需要先對fetch的res進行res.json()之後才拿到我們想要的json資料,而axios幫我們做了,使用起來更簡單~

這算是一個缺點吧~但是!!!我們通過封裝完全可以解決的,接下來就是為啥這也是fetch的優點了,首先來說說為啥fetch需要兩層才可以,第一層進行的是res.json(),因為fetch的response是一個綜合各種方法的物件,並不是請求的資料,也就是說其實fetch不僅可以是一個json物件,還可以是其他很多的,如果我們需要的是json物件,就res.json(),如果需要的是文字字串,可以res.text(),它還可以直接獲取blob內容,res.blob()。綜上所述,fetch是真的真的很強大,就看你擅不擅長封裝使用了。

事先宣告,我上面吹了那麼久,其實我個人覺得我也不算太會使用哈,下面的封裝也就是簡單的封裝,各位看官按需使用,可以在我基礎之上針對自己的專案進行更好的封裝,或者就直接自己封裝就可以了。

fetch的缺點

  • 獲取資料的方式需要兩層
  • fetch只對網路請求報錯,對400,500都當做成功的請求
  • fetch不會攜帶cookie,需要新增配置
  • fetch不支援timeout ...

上面講了fetch的幾個缺點,其實這些缺點都是可以通過封裝以及外掛來進行彌補的。

封裝fetch的流程以及目的

  • 人性化的使用方式,get, post, put, delete, patch等方法的呼叫
  • 滿足大部分場景的資料獲取方式,也就是直接res.json()
  • 支援timeout
  • 錯誤處理
  • 配合日誌元件

封裝程式碼

import fetch from 'isomorphic-unfetch';
import qs from 'query-string';
import { filterObject } from './util';

// initial fetch
const nextFetch = Object.create(null);
// browser support methods
// ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PATCH', 'PUT']
const HTTP_METHOD = ['get', 'post', 'put', 'patch', 'delete'];
// can send data method
const CAN_SEND_METHOD = ['post', 'put', 'delete', 'patch'];

HTTP_METHOD.forEach(method => {
  // is can send data in opt.body
  const canSend = CAN_SEND_METHOD.includes(method);
  nextFetch[method] = (path, { data, query, timeout = 5000 } = {}) => {
    let url = path;
    const opts = {
      method,
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
        Accept: 'application/json'
      },
      credentials: 'include',
      timeout,
      mode: 'same-origin',
      cache: 'no-cache'
    };

    if (query) {
      url += `${url.includes('?') ? '&' : '?'}${qs.stringify(
        filterObject(query, Boolean),
      )}`;
    }

    if (canSend && data) {
      opts.body = qs.stringify(filterObject(data, Boolean));
    }

    console.info('Request Url:', url);

    return fetch(url, opts)
      .then(res => res.json())
      .then(({ errcode = 0, errmsg, data }) => {
        if (errcode !== 0) {
          const err = new Error(errmsg);
          err.message = errmsg;
          err.code = errcode;
          err.data = data;
          throw err;
        }
        return data;
      });
  };
});

export default nextFetch;
複製程式碼

封裝完之後的使用:

// /redux/sagas/user/userList.js
...
import nextFetch from '../../../core/nextFetch';

export function* userList() {
  while (true) {
    yield take(FETCH_USER_LIST);
    try {
      // 新寫法
      const data = yield nextFetch.get(api.getUserList);
      // 原來的寫法
      // const res = yield fetch(api.getUserList);
      // const { data } = yield res.json();
      yield put(fetchUserListDataSuccess(data));
    } catch (error) {
      console.log(error.code, error.message, error.data);
      yield put(fetchUserListDataFali(error));
    }
  }
}
複製程式碼

為什麼使用的是isomorphic-unfetch, 因為Next.js是服務端渲染,這個標榜的是前後端都通用~以前我用的服務端渲染框架因為是分離的,前後端使用的是不同的fetch(前端是whatwg-fetch,後端是node-fetch)。

上面是我自己的封裝方式,我預設後端返回的是一個json格式的資料,然後裡面有三個欄位{ data, errcode, errmsg },並且成功的響應code = 0。小夥伴們根據自己的專案結構適當進行更改。

增加中介軟體

這個就很簡單了,redux的中介軟體系統,為什麼增加中介軟體呢,因為上面我們增加了fetch的error處理,那麼我們就可以對失敗的請求在中介軟體這個地方進行提前處理~

// /redux/store.js
import userMiddleware from '../middlewares/client/user';
...
// 增加redux中介軟體
const bindMiddleware = (middleware) => {
  // add route middleware
  middleware.push(userMiddleware);
  ...
  return applyMiddleware(...middleware);
};

// /middles/client/user.js
import { message } from 'antd';
import {
  FETCH_USER_LIST_FAIL
} from '../../constants/ActionTypes';

export default ({ getState }) => next => action => {
  // redux中介軟體
  const ret = next(action);
  switch (action.type) {
    case FETCH_USER_LIST_FAIL: {
      message.error('獲取使用者列表失敗, 請稍後重試');
      break;
    }
    default:
  }
  return ret;
};

複製程式碼

效果如圖所示:

Next.js腳手架進階 — 封裝fetch && 增加中介軟體

結論

今天這個就很簡單了,也不是什麼高階的東西,都是隨手拿來用的,只不過封裝起來方便使用一些,感覺這個腳手架到這真的可以直接進行專案的開發了,可能還差一個前端日誌功能吧,下面有時間寫寫前端日誌~

程式碼地址

相關文章