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

复制代码

效果如图所示:

结论

今天这个就很简单了,也不是什么高级的东西,都是随手拿来用的,只不过封装起来方便使用一些,感受这个脚手架到这真的能够直接进行项目的开发了,可能还差一个前端日志功能吧,下面有时间写写前端日志~

代码地址

相关文章
相关标签/搜索