Next.js腳手架進階 — 封裝fetch && 增長中間件

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的區別node

// 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幫咱們作了,使用起來更簡單~react

這算是一個缺點吧~可是!!!咱們經過封裝徹底能夠解決的,接下來就是爲啥這也是fetch的優勢了,首先來講說爲啥fetch須要兩層才能夠,第一層進行的是res.json(),由於fetch的response是一個綜合各類方法的對象,並非請求的數據,也就是說其實fetch不只能夠是一個json對象,還能夠是其餘不少的,若是咱們須要的是json對象,就res.json(),若是須要的是文本字符串,能夠res.text(),它還能夠直接獲取blob內容,res.blob()。綜上所述,fetch是真的真的很強大,就看你擅不擅長封裝使用了。ios

事先聲明,我上面吹了那麼久,其實我我的以爲我也不算太會使用哈,下面的封裝也就是簡單的封裝,各位看官按需使用,能夠在我基礎之上針對本身的項目進行更好的封裝,或者就直接本身封裝就能夠了。git

fetch的缺點

  • 獲取數據的方式須要兩層
  • fetch只對網絡請求報錯,對400,500都當作成功的請求
  • fetch不會攜帶cookie,須要添加配置
  • fetch不支持timeout ...

上面講了fetch的幾個缺點,其實這些缺點都是能夠經過封裝以及插件來進行彌補的。github

封裝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;
複製代碼

封裝完以後的使用:express

// /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

上面是我本身的封裝方式,我默認後端返回的是一個json格式的數據,而後裏面有三個字段{ data, errcode, errmsg },而且成功的響應code = 0。小夥伴們根據本身的項目結構適當進行更改。redux

增長中間件

這個就很簡單了,redux的中間件系統,爲何增長中間件呢,由於上面咱們增長了fetch的error處理,那麼咱們就能夠對失敗的請求在中間件這個地方進行提早處理~axios

// /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;
};

複製代碼

效果如圖所示:

結論

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

代碼地址

相關文章
相關標籤/搜索