六九、ajax,fetch,axios,wx.request封裝

荼荼荼蘼發表於2018-08-27

前端開發不可避免的介面請求,而在使用原生ajax,或者其他包的時候要麼功能不全,要麼包太大,所有統一封裝一套供日常開發使用。

axios封裝

  • 推薦vue框架中使用
// index.js
import axios from 'axios'
import { Toast } from 'vant'

// 建立一個錯誤
function errorCreate (msg) {
  const error = new Error(msg)
  errorLog(error)
  throw error
}

// 記錄和顯示錯誤
function errorLog (error) {
  // 列印到控制檯
  if (process.env.NODE_ENV === 'development') {
    console.log('>>>>>> Error >>>>>>')
    console.log(error)
  }
  // 顯示提示
  Toast({
    message: error.message,
    type: 'error',
    duration: 5 * 1000
  })
}

// 建立一個 axios 例項
// const host = window.location.host
const service = axios.create({
  baseURL: process.env.NODE_ENV === 'development' ? 'http://192.168.1.200:81' : 'http://video.uniqorn.com.cn',
  timeout: 5000 // 請求超時時間
})

// 請求攔截器
service.interceptors.request.use(
  config => {
    // 在請求傳送之前做一些處理
    // const token = util.cookies.get('token')
    // 讓每個請求攜帶token-- ['X-Token']為自定義key 請根據實際情況自行修改
    // config.headers['X-Token'] = token
    return config
  },
  error => {
    // 傳送失敗
    console.log(error)
    Promise.reject(error)
  }
)

// 響應攔截器
service.interceptors.response.use(
  response => {
    // dataAxios 是 axios 返回資料中的 data
    const dataAxios = response.data
    // 這個狀態碼是和後端約定的
    const { code } = dataAxios
    // 根據 code 進行判斷
    if (code === undefined) {
      // 如果沒有 code 代表這不是專案後端開發的介面 比如可能是 D2Admin 請求最新版本
      return dataAxios
    } else {
      // 有 code 代表這是一個後端介面 可以進行進一步的判斷
      switch (code) {
        case 200:
          // [ 示例 ] code === 0 代表沒有錯誤
          return dataAxios.data
        case -403:
          Toast('未登入,請先登入!')
          // [ 示例 ] 其它和後臺約定的 code
          // router.push({
          //   name: 'login'
          // })
          break
        default:
          // 不是正確的 code
          errorCreate(`${dataAxios.msg}: ${response.config.url}`)
          break
      }
    }
  },
  error => {
    if (error && error.response) {
      switch (error.response.status) {
        case 400:
          error.message = '請求錯誤'
          break
        case 401:
          error.message = '未授權,請登入'
          break
        case 403:
          error.message = '拒絕訪問'
          break
        case 404:
          error.message = `請求地址出錯: ${error.response.config.url}`
          break
        case 408:
          error.message = '請求超時'
          break
        case 500:
          error.message = '伺服器內部錯誤'
          break
        case 501:
          error.message = '服務未實現'
          break
        case 502:
          error.message = '閘道器錯誤'
          break
        case 503:
          error.message = '服務不可用'
          break
        case 504:
          error.message = '閘道器超時'
          break
        case 505:
          error.message = 'HTTP版本不受支援'
          break
        default:
          break
      }
    }
    errorLog(error)
    return Promise.reject(error)
  }
)

class Http {
  $http (params) {
    // const token = util.cookies.get('token')
    // if (token) params.data.token = token
    return new Promise((resolve, reject) => {
      !params.isNoLoading && Toast.loading({
        duration: 0, // 持續展示 toast
        forbidClick: true,
        message: '...'
      })
      service({
        method: params.method || 'get',
        url: params.url,
        header: {
          'content-type': 'application/x-www-form-urlencoded'
        },
        params: params.data || {}
      }).then((res) => {
        resolve(res)
      }).catch(e => {
        reject(e)
      }).finally(() => {
        Toast.clear() //關閉loading
      })
    })
  }
}

export default Http

// api.js
import Fetch from './index'

class Api extends Fetch {
  // eslint-disable-next-line no-useless-constructor
  constructor () {
    super()
  }

  /**
   *獲取視訊詳情
   * @returns {*}
   * @private
   */
  _getVideoInfo (data) {
    return this._init('/api/video/info', data, false)
  }

  _init (url, data, method, isNoLoading) {
    const params = {
      url,
      data,
      method,
      isNoLoading, //是否顯示loading
    }
    return this.$http(params)
  }
}

export default Api

//main.js
// 定義全域性api 也可使用install 定義use
import Api from './api/api'
Vue.prototype.Api = new Api()

// 頁面中使用
async _handleVideoInfo() {
    let res = await this.Api._getVideoInfo({videoId:1})
    console.log(res)
}      

main.js也可封裝為外掛使用


ajax封裝

  • 適用於所有框架或者原生開發
const config={
    IS_DEV_ENV : process.env.NODE_ENV !== 'production',
    XHR_RES_CODE : {
        SUCCESS: 0,
        REDIRECT: 110
    }
}
const ajax = {
    _request: (options) => {
        return new Promise((onResolve, onReject) => {
            const xhr = new XMLHttpRequest();
            xhr.onreadystatechange = () => {
                if (xhr.readyState.toString() === '4') {
                    let response;
                    try {
                        response = JSON.parse(xhr.responseText);
                    }
                    catch (err) {
                        response = xhr.responseText;
                    }

                    if (xhr.status >= 200 && xhr.status < 400) {
                        const isRedirect = typeof response.err !== 'undefined' && response.err === config.XHR_RES_CODE.REDIRECT;
                        const isSuccess = typeof response.err !== 'undefined' && response.err === config.XHR_RES_CODE.SUCCESS;
                        if (isRedirect) {
                            onResolve(response);
                            window.location.href = config.IS_DEV_ENV ? '../login/' : '/login';
                        }
                        else if (isSuccess) {
                            onResolve(response);
                        }
                        else {
                            onReject(response);
                        }
                    }
                    else {
                        onReject(response);
                    }
                }
            };

            if (options.type.toLowerCase() === 'get' && options.params != null) {
                const qs = Object.keys(options.params).map((key) => {
                    return `${key}=${options.params[key]}`;
                }).join('&').trim();

                options.url = qs === '' ? `${options.url}` : `${options.url}?${qs}`;
                options.params = null;
            }

            xhr.open(options.type, options.url, true);

            if (options.header) {
                xhr.setRequestHeader('Content-Type', options.header);
            }

            xhr.timeout = 1000 * 30;
            xhr.send(options.params);
        });
    },
    get: (options) => {
        options.type = 'GET';
        return ajax._request(options);
    },
    post: (options) => {
        options.type = 'POST';
        options.header = 'application/json';
        options.params = JSON.stringify(options.params);
        return ajax._request(options);
    }
};

export default ajax;


fetch封裝

  • 推薦react框架中使用
const CREDS = 'include'
function checkStatus (response) {
  if (response.status >= 200 && response.status < 300) { return response }
  const error = new Error(response.statusText)
  error.response = response
  throw error
}
const asyncFetch = async function (obj) {
  const url = obj.url || obj
  const method = obj.method || 'GET'
  const credentials = obj.credentials || CREDS
  const body = obj.obj || null
  let confFetch = { method, credentials }
  if (method === 'POST') { confFetch = { method, credentials, body: JSON.stringify(body) } }
  return new Promise(function (resolve, reject) {
    fetch(url, confFetch)
      .then(checkStatus)
      .then(res => res.json())
      .then(res => { resolve(res) })
      .catch(err => { reject(err) })
  })
}

export default asyncFetch

wx.request封裝

  • 小程式中使用
//fetch.js
export default  class Fetch {
  constructor() {
    this.host = https; /* 全域性域名*/
  }

  $http(params) {
    let sessionId = wx.getStorageSync('sessionId')
    !params.isNoLoading && wx.showLoading({
      title: params.loadingText || '資料載入中...',
      mask: 'true'
    })
    return new Promise((resolve, reject) => {
      wx.request({
        method: params.method || 'GET',
        url: `${this.host}${params.url}?sessionId=${sessionId}`,
        header: {
          'content-type': params.contentType || 'application/json' || 'application/x-www-form-urlencoded'
        },
        data: params.data || {},
        success: (res) => {
          if (res.data.status === 200) {
            resolve(res.data.data)
          } else {
            reject(res.data.msg)
            wx.showToast({
              title: res.data.msg,
              icon: 'none',
              duration: 2000
            })
          }
        },
        fail() {
          reject
        },
        complete() {
          wx.hideLoading()
        }
      })
    })
  }
}


api.js
import Fetch from './fetch'

class Api extends Fetch {
  constructor() {
    super()
  }
/**
   * 取消關注
   * @param data -delete
   * @returns {Promise<any>}
   * @private
   */
  _cancelFollow(data) {
    const params = {
      url: '/app/follow/deleteByBeOpenId',
      data,
      method: 'DELETE',
      isNoLoading: true
    }
    return this.$http(params)
  }
}
export default {
    API: new Api()
}


相關文章