照例先贴上 https://github.com/axios/axios 的 gitHub 地址
不管用什么方式获取数据, 对于一个项目来说, 代码一定要利于维护其次是一定要写的优美, 因此加上一层封装是必要的
vuejs2.0 已经不再维护 vue-resource,vuejs2.0 已经使用了 axios, 这也是为什么我会转到 axios 的主要原因, 废话不多说:
基本的封装要求:
统一 url 配置
统一 api 请求
request (请求)拦截器, 例如: 带上 token 等, 设置请求头
response (响应)拦截器, 例如: 统一错误处理, 页面重定向等
根据需要, 结合 Vuex 做全局的 loading 动画, 或者错误处理
将 axios 封装成 Vue 插件使用
文件结构
使用 vue-cli 进行相关的封装, 在 src 文件夹下:
src
|
-- http 封装 axios 模块文件夹
|
---- config.js axios 的默认配置
---- api.js 二次封装 axios, 拦截器等
---- interface.js 请求接口文件
---- index.js 将 axios 封装成插件
config.js
默认配置参照 gitHub, 以下只做示例:
- export default {
- method: 'post',
- // 基础 url 前缀
- baseURL: 'https://easy-mock.com/mock/5ad75e9f41d4d65f0e935be4/example',
- // 请求头信息
- headers: {
- 'Content-Type':'application/json;charset=UTF-8'
- },
- // 参数
- data: {},
- // 设置超时时间
- timeout: 10000,
- // 携带凭证
- withCredentials: false,
- // 返回数据类型
- responseType: 'json'
- }
PS: 这里推荐一下一款 Mock 工具 Easy Mock https://easy-mock.com/ , 以上请求地址来自该工具. 以后有空会单独写一下怎么使用该工具.
- api.js
- import axios from 'axios' // 注意先安装哦
- import config from './config.js' // 倒入默认配置
- import qs from 'qs' // 序列化请求数据, 视服务端的要求
- export default function $axios (options) {
- return new Promise((resolve, reject) => {
- const instance = axios.create({
- baseURL: config.baseURL,
- headers: {},
- transformResponse: [function (data) {}]
- }
- )
- // request 拦截器
- instance.interceptors.request.use(
- config => {
- // Tip: 1
- // 请求开始的时候可以结合 vuex 开启全屏的 loading 动画
- // Tip: 2
- // 带上 token , 可以结合 vuex 或者重 localStorage
- // if (store.getters.token) {
- // config.headers['X-Token'] = getToken() // 让每个请求携带 token--['X-Token']为自定义 key 请根据实际情况自行修改
- // } else {
- //// 重定向到登录页面
- // }
- // Tip: 3
- // 根据请求方法, 序列化传来的参数, 根据后端需求是否序列化
- if (config.method.toLocaleLowerCase() === 'post'
- || config.method.toLocaleLowerCase() === 'put'
- || config.method.toLocaleLowerCase() === 'delete') {
- config.data = qs.stringify(config.data)
- }
- return config
- },
- error => {
- // 请求错误时做些事(接口错误, 超时等)
- // Tip: 4
- // 关闭 loadding
- console.log('request:', error)
- // 1. 判断请求超时
- if (error.code === 'ECONNABORTED' && error.message.indexOf('timeout') !== -1) {
- console.log('根据你设置的 timeout / 真的请求超时 判断请求现在超时了, 你可以在这里加入超时的处理方案')
- // return service.request(originalRequest);// 例如再重复请求一次
- }
- // 2. 需要重定向到错误页面
- const errorInfo = error.response
- console.log(errorInfo)
- if (errorInfo) {
- // error =errorInfo.data// 页面那边 catch 的时候就能拿到详细的错误信息, 看最下边的 Promise.reject
- const errorStatus = errorInfo.status; // 404 403 500 ... 等
- router.push({
- path: `/error/${errorStatus}`
- })
- }
- return Promise.reject(error) // 在调用的那边可以拿到 (catch) 你想返回的错误信息
- }
- )
- // response 拦截器
- instance.interceptors.response.use(
- response => {
- let data;
- // IE9 时 response.data 是 undefined, 因此需要使用 response.request.responseText(Stringify 后的字符串)
- if(response.data == undefined){
- data = response.request.responseText
- } else{
- data = response.data
- }
- // 根据返回的 code 值来做不同的处理(和后端约定)
- switch (data.code) {
- case '':
- break;
- default:
- }
- // 若不是正确的返回 code, 且已经登录, 就抛出错误
- // const err = new Error(data.description)
- // err.data = data
- // err.response = response
- // throw err
- return data
- },
- err => {
- if (err && err.response) {
- switch (err.response.status) {
- case 400:
- err.message = '请求错误'
- break
- case 401:
- err.message = '未授权, 请登录'
- break
- case 403:
- err.message = '拒绝访问'
- break
- case 404:
err.message = ` 请求地址出错: ${err.response.config.url}`
- break
- case 408:
- err.message = '请求超时'
- break
- case 500:
- err.message = '服务器内部错误'
- break
- case 501:
- err.message = '服务未实现'
- break
- case 502:
- err.message = '网关错误'
- break
- case 503:
- err.message = '服务不可用'
- break
- case 504:
- err.message = '网关超时'
- break
- case 505:
- err.message = 'HTTP 版本不受支持'
- break
- default:
- }
- }
- console.error(err)
- // 此处我使用的是 element UI 的提示组件
- // Message.error(`ERROR: ${err}`);
- return Promise.reject(err) // 返回接口返回的错误信息
- }
- )
- // 请求处理
- instance(options)
- .then((res) => {
- resolve(res)
- return false
- })
- .catch((error) => {
- reject(error)
- })
- })
- }
- interface.js
- import axios from './api' // 倒入 api
/* 将所有接口统一起来便于维护
* 如果项目很大可以将 url 独立成文件, 接口分成不同的模块
* 此处的数据依然来自 Easy Mock
*/
- // 单独倒出
- export const query = params => {
- return axios({
- url: '/query',
- method: 'get',
- params
- })
- }
- export const mock = params => {
- return axios({
- url: '/mock',
- method: 'get',
- params
- })
- }
- export const upload = data => {
- return axios({
- url: '/upload',
- method: 'post',
- data
- })
- }
- // 默认全部倒出
- // 根绝需要进行
- export default {
- query,
- mock,
- upload
- }
- index.js
封装成 Vue 插件, 便 (提) 于(高)使 (B) 用(格)
- // 倒入所有接口
- import apiList from './interface'
- const install = Vue => {
- if (install.installed)
- return;
- install.installed = true;
- Object.defineProperties(Vue.prototype, {
- // 注意哦, 此处挂载在 Vue 原型的 $api 对象上
- $api: {
- get() {
- return apiList
- }
- }
- })
- }
- export default install
使用
到此为止, 万事俱备就差用了, 在 mian.js 中做如下操作:
- // 倒入 http 文件夹下的 index.js
- import api from './http/index'
- Vue.use(api)
- // 此时可以直接在 Vue 原型上调用 $api 了
总结
以上二次封装较为全面, 基本完成了我们之前的需求
在错误的处理上还需要与后端协定好返回值, 做具体的约定
封装回调有点多, 在使用的时候也需要加上 then() 来处理结果, async & await 了解一下哟, 好东西当然要藏起来, 我才不会分享出来呢...
PS: IE9 不支持 Promise 哦, 需要安装一个 polyfill
import 'babel-polyfill'