Vue自用ES6 Async 风格的api请求文件封装(fetch,axios,post,get)

api调用实例

...
methed: {
  ...
  // 根据账户查询用户的api
  async getUser() {
    const {account} = this
    const data = {
      account 
    }
    await this.$api
              .getUsers({
                data,
                // 成功回调
                onSuccess: res => {
                  // ?. 为选择链式调用, 如果data为null, 不会报错,会返回undefined, 需要polify
                  let list = res ?.data ?.pageData || []
                  // 处理数据
                  list.forEach((item, index) => {
                    item.children && delete item.children;
                    item.key = index
                  });
                  this.list = list;
                }
              })
  },
  mounted() {
    this.loading = true
    await this.getUser()

    await this.anotherApi
    await this.anotherApi
    await this.anotherApi

    this.loading = false
  }
  ...
}

请求前, 可以打开loading开关, await 调用接口请求函数, 可以在请求完成后, 再继续运行程序, 关闭loading开关

fetch.js封装过程

  • axios

  import axios from 'axios'

axios中文文档

axios 是一个基于 promise 用于浏览器和 nodejs 的 http 客户端,本身具有以下特征:

  • 从浏览器中创建 XMLHttpRequest
  • 从 nodejs 发出 http 请求
  • 支持 Promise API
  • 拦截请求和响应
  • 转换请求和响应数据
  • 取消请求
  • 自动转换 JSON 数据
  • 客户端支持防止 CSRF / XSRF

axios 对vue有非常高的支持度, 很多vue项目都是用axios作为api请求插件. 网上也有非常多对axios进行二次封装的文章, 笔者也是参考了总多封装方法, 最后自己修改设计了这套自用模板. axios的具体是用方法此处就不详细介绍, 如果对axios还不是非常熟悉, 可以直接访问文档进行学习.

除了axios, 也还有fetch等其他api请求方案, 也可以根据不同的请求方案, 对fetch.js文件进行修改, 以满足需求.

  • message

import {
  notification
} from 'ant-design-vue'
...
function message(msg) {
  notification.error({
    message: "服务器请求异常",
    description: "提示:" + msg
  })
}

message 模块是用于接口提示信息抛出的模块. 比如, 一个修改数据的接口, 在接口请求成功后, 返回成功code. 此时就可以用message弹出一个提示信息, 提示用户修改成功. 请求失败也是同理, 可以抛出失败原因等.可以根据项目, 修改对应的组件. 例如element的 messageBox等.

  • defaultSetting

// 默认请求配置
const defaultSetting = {
  // 请求方法
  method: 'GET',
  // 超时时间ms
  timeout: 600000,
  // res.code成功判定范围
  statusMin: 0,
  statusMax: 99999,
  // 全局失败回调
  onGlobalFail: function (res) {
    message(res.message || res.msg)
  },
  // 成功条件计算方法
  computeSuccess: function (res) {
    if (res.status > 299) return false
    return 1 === res.data.code
  }
}

defaultSetting默认设置, 这里可以配置一些默认参数

  • method 请求方法(GET, PSOT, ...)

  • timeout 请求超时判断时间ms

  • statusMin, statusMax 请求返回状态码判定成功的范围,例如min设置为200, max设置为399. 则403, 404等状态码的请求会被判定为请求失败, 执行请求失败分支.(这个状态码配置的是aixos插件的成功判断, 由于笔者自己写了成功与失败的分支, 所以这里设置了0-99999都为成功, 即所有情况下都把结果返回, 由自定义的方法去判断请求是否成功)

  • onGlobalFail [Function] 全局失败回调. 这个方法可以接受一个res的参数,即请求结果. 当自定义方法判定请求失败时执行.此方法默认为全局方法, 会应用于所有请求. 这个方法默认为使用之前定义的message方法, 弹出一个提示框, 展示失败信息. 可以在调用具体接口时覆盖此方法.

  • computeSuccess 此方法用于计算成功状态,接受res参数, 即请求结果. 此方法返回一个boolean值, true时执行请求成功逻辑, false时执行请求失败逻辑.

  • config


export default async ({
  // 请求地址
  url = "",
  // `data` 是作为请求主体被发送的数据
  // 在没有设置 `transformRequest` 时,必须是以下类型之一:
  // - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
  // - 浏览器专属:FormData, File, Blob
  // - Node 专属: Stream
  data = {},
  // "GET" "POST" ...
  method = defaultSetting.method,
  // `timeout` 指定请求超时的毫秒数(0 表示无超时时间)
  // 如果请求话费了超过 `timeout` 的时间,请求将被中断
  timeout = defaultSetting.timeout,
  // `withCredentials` 表示跨域请求时是否需要使用凭证
  withCredentials = false,
  // `maxContentLength` 定义允许的响应内容的最大尺寸
  maxContentLength = 10000,
  // `responseType` 表示服务器响应的数据类型,可以是 'arraybuffer', 'blob', 'document', 'json', 'text', 'stream'
  responseType = "json",
  validateStatus = (status) => {
    return status >= defaultSetting.statusMin && status < defaultSetting.statusMax; // 默认的
  },
  // `onUploadProgress` 允许为上传处理进度事件
  onUploadProgress = () => {},
  // `onDownloadProgress` 允许为下载处理进度事件
  onDownloadProgress = () => {},
  // `cancelToken` 指定用于取消请求的 cancel token
  // (查看后面的 Cancellation 这节了解更多)
  cancelToken,
  // `params` 是即将与请求一起发送的 URL 参数
  // 必须是一个无格式对象(plain object)或 URLSearchParams 对象
  params,
  // `auth` 表示应该使用 HTTP 基础验证,并提供凭据
  // 这将设置一个 `Authorization` 头,覆写掉现有的任意使用 `headers` 设置的自定义 `Authorization`头
  auth,
  baseUrl,
  // `headers` 是即将被发送的自定义请求头
  headers = {},
  // 成功回调
  onSuccess = function (res) {},
  // 失败回调
  onFail = function (res) {},
  // 全局回调(无论成功失败, 都执行)
  onFinally = function (res) {},
  // 全局成功回调
  onGlobalSuccess = function (res) {},
  // 全局失败回调
  onGlobalFail = defaultSetting.onGlobalFail,
  // 成功条件计算方法
  computeSuccess = defaultSetting.computeSuccess
}) => {...}

这里为fetch.js导出的fetch方法的参数config.其中部分参数被使用于axios请求当中.部分参数被使用于请求后成功与失败逻辑执行.

  • 请求

 let _baseUrl = baseUrl || defaultBaseUrl
  // 如果有redirectUrl, 则覆盖url
  url = _baseUrl + url;
  // 此处规定get请求的参数使用时放在data中,如同post请求
  if (method == "GET") {
    let dataStr = "";
    // 将data参数用?&拼接
    dataStr = Object.entries(data).reduce((dataStr, value) => {
      dataStr = dataStr + `${value[0]}=${value[1]}&`
      return dataStr
    }, dataStr)
    if (dataStr !== "") {
      dataStr = dataStr.substr(0, dataStr.lastIndexOf("&"));
      url = url + "?" + dataStr;
    }
  }

此处拼接url

// config
let requestConfig = {
  url,
  method: method, // 默认是 get
  headers,
  params,
  data,
  timeout,
  withCredentials,
  auth,
  responseType, // 默认的
  onUploadProgress,
  onDownloadProgress,
  maxContentLength,
  cancelToken,
  validateStatus
}

用于aixos请求的config

// 发送请求
await axios(requestConfig).then(res => {
  // 允许实例自行计算成功条件
  const isSuccess = computeSuccess(res)
  const result = res.data
  // 成功码默认为1, 根据后台修改
  if (isSuccess) {
    onGlobalSuccess(result)
    onSuccess(result)
  } else {
    onGlobalFail(result)
    onFail(result)
  }
  onFinally(res)
})

发送请求, 通过computeSuccess计算出成功状态码, 运行对应分支逻辑

  • 完整的fetch.js

import axios from 'axios'

import {
  notification
} from 'ant-design-vue'

const defaultBaseUrl = "http://192.168.0.1"; // 线上
// 默认请求配置
const defaultSetting = {
  // 请求方法
  method: 'GET',
  // 超时时间ms
  timeout: 600000,
  // res.code成功判定范围
  statusMin: 200,
  statusMax: 99999,
  // 全局失败回调
  onGlobalFail: function (res) {
    message(res.message || res.msg)
  },
  // 成功条件计算方法
  computeSuccess: function (res) {
    if (res.status > 299) return false
    return 1 === res.data.code
  }
}
// 服务器报错提示款, 可以根据框架修改此函数, msg为抛出信息
function message(msg) {
  notification.error({
    message: "服务器请求异常",
    description: "提示:" + msg
  })
}
export default async ({
  // 请求地址
  url = "",
  // `data` 是作为请求主体被发送的数据
  // 在没有设置 `transformRequest` 时,必须是以下类型之一:
  // - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
  // - 浏览器专属:FormData, File, Blob
  // - Node 专属: Stream
  data = {},
  // "GET" "POST" ...
  method = defaultSetting.method,
  // `timeout` 指定请求超时的毫秒数(0 表示无超时时间)
  // 如果请求话费了超过 `timeout` 的时间,请求将被中断
  timeout = defaultSetting.timeout,
  // `withCredentials` 表示跨域请求时是否需要使用凭证
  withCredentials = false,
  // `maxContentLength` 定义允许的响应内容的最大尺寸
  maxContentLength = 10000,
  // `responseType` 表示服务器响应的数据类型,可以是 'arraybuffer', 'blob', 'document', 'json', 'text', 'stream'
  responseType = "json",
  validateStatus = (status) => {
    return status >= defaultSetting.statusMin && status < defaultSetting.statusMax; // 默认的
  },
  // `onUploadProgress` 允许为上传处理进度事件
  onUploadProgress = () => {},
  // `onDownloadProgress` 允许为下载处理进度事件
  onDownloadProgress = () => {},
  // `cancelToken` 指定用于取消请求的 cancel token
  // (查看后面的 Cancellation 这节了解更多)
  cancelToken,
  // `params` 是即将与请求一起发送的 URL 参数
  // 必须是一个无格式对象(plain object)或 URLSearchParams 对象
  params,
  // `auth` 表示应该使用 HTTP 基础验证,并提供凭据
  // 这将设置一个 `Authorization` 头,覆写掉现有的任意使用 `headers` 设置的自定义 `Authorization`头
  auth,
  baseUrl,
  // `headers` 是即将被发送的自定义请求头
  headers = {},
  // 成功回调
  onSuccess = function (res) {},
  // 失败回调
  onFail = function (res) {},
  // 全局回调(无论成功失败, 都执行)
  onFinally = function (res) {},
  // 全局成功回调
  onGlobalSuccess = function (res) {},
  // 全局失败回调
  onGlobalFail = defaultSetting.onGlobalFail,
  // 成功条件计算方法
  computeSuccess = defaultSetting.computeSuccess
}) => {
  let _baseUrl = baseUrl || defaultBaseUrl
  // 如果有redirectUrl, 则覆盖url
  url = _baseUrl + url;
  // 此处规定get请求的参数使用时放在data中,如同post请求
  if (method == "GET") {
    let dataStr = "";
    // 将data参数用?&拼接
    dataStr = Object.entries(data).reduce((dataStr, value) => {
      dataStr = dataStr + `${value[0]}=${value[1]}&`
      return dataStr
    }, dataStr)
    if (dataStr !== "") {
      dataStr = dataStr.substr(0, dataStr.lastIndexOf("&"));
      url = url + "?" + dataStr;
    }
  }
  // config
  let requestConfig = {
    url,
    method: method, // 默认是 get
    headers,
    params,
    data,
    timeout,
    withCredentials,
    auth,
    responseType, // 默认的
    onUploadProgress,
    onDownloadProgress,
    maxContentLength,
    cancelToken,
    validateStatus
  }
  // 发送请求
  await axios(requestConfig).then(res => {
    // 允许实例自行计算成功条件
    const isSuccess = computeSuccess(res)
    const result = res.data
    // 成功码默认为1, 根据后台修改
    if (isSuccess) {
      onGlobalSuccess(result)
      onSuccess(result)
    } else {
      onGlobalFail(result)
      onFail(result)
    }
    onFinally(res)
  })
};

api.js

import fetch from "./fetch.js";

export const api = {
  // 1. /login
  loginV2: config =>
    fetch({
      ...config,
      url: "/app/user/loginV2",
      onGlobalFail: function (res) {
        Modal.error({
          title: '登录失败',
          content: res.message || res.msg
        })
      },
      method: "POST"
    }),
  // 2. 获取用户  
  getUser: config =>
    fetch({
      ...config,
      url: "/app/user/findUser",
      method: "POST"
    }),
  ...
};

引入fetch.js, 所有接口添加到api对象中导出. 这里还可以对单个接口进行单独的拦截. 比如所有getUser方法对应的接口不需要弹窗提示, 那么可以在这里修改getUser

  ...
  getUser: config =>
    fetch({
      ...config,
      onGlobalFail: () => {},
      url: "/app/user/findUser",
      method: "POST"
    }),
  ...

这样, 如果这个接口在多个页面都进行了调用, 则无需挨个修改了.

main.js

...
import {
  api
} from './api/api'

Vue.prototype.$api = api;
...

将api挂载到Vue原型

以上, 完成这行动作后, 我们就能通过this.$api这种方式调用接口了.

mock

开发时,我们经常需要一些虚拟数据.那么, 我们可以写一个mock.js用于mock数据.

mock.js

export const loginData = {
  "code": 1,
  "msg": "success",
  "data": {
    "userId": 1,
    "account": "2222222222",
    "role": 3,
    "password": null,
    "phone": "21121211221",
    "email": null,
    "unionId": null,
    "reName": null,
    "nickName": "xxx2",
    "accId": "dee2d504239649b6be54c1279926a4a7",
    "ytxAccount": "852b83a42d424f36bb9ef7d383acc08d",
    "headPic": "http://k1VoaL3.wlekjwqejklwq",
    "gender": 0,
    "age": null,
    "birthday": null,
    "constellation": null,
    "bloodType": null,
    "profession": null,
    "address": null,
    "contact": null,
    "studyCfNum": null,
    "teacherCfNum": null,
    "state": 0,
    "createTime": "2016-12-27 13:20:43",
    "lastUpdateTime": "2019-12-12 09:01:59",
    "lastLoginTime": "2020-01-02 17:50:14",
    "balance": null,
    "orgno": "00000768",
    "schoolName": null,
    "isBind": null,
    "onlineState": null,
    "userDevices": [],
    "classList": [],
    "clazzList": [],
    "children": [],
    "usrChildren": [],
    "fileStore": null,
    "popenId": "oFwyg4vN-Wv9LMwNKafeeeeeWexM",
    "topenId": "ohH6X5JSLwHzwYNewqewqecagVeaw"
  }
}

mockedApi

import fetch from "./fetch.js";
import {
  loginData
} from './mock.js'
export const api = {
  // 1. /login
  loginV2: config => config.onSuccess(loginData),
};

修改main.js中的api导入路径为 mockedApi.js. 这样, 当后台接口开发完毕, 只要把路径再改回去正式的接口这一个步骤, 就完成了mock数据到正式接口数据的变更, 十分便利.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342