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;
};
複製程式碼
效果如圖所示:
結論
今天這個就很簡單了,也不是什麼高階的東西,都是隨手拿來用的,只不過封裝起來方便使用一些,感覺這個腳手架到這真的可以直接進行專案的開發了,可能還差一個前端日誌功能吧,下面有時間寫寫前端日誌~