axios封裝api

chavesgu發表於2018-08-10

前言

本文涉及到axios,vuex,vuex/modules並且沒有覆蓋所有的專案情況,如果不符合你的技術棧,還請隨便看看。

如果不願意看作者扯淡,可以看github上的demo(typescript)

另外,這個demo是使用了vue-cli@3.0tsclass寫的,如果不熟悉語法的,還請擔待。本文章的語法會依然使用js。

不建議在vue中使用ts,目前還沒有完全相容,元件中使用vuex的action也會丟失型別監測等等,感覺ts的語法相比eslint的檢測更適合團隊。

歡迎來我的Vue技術群交流:Vue887516034

Axios

首先先封裝需要使用的請求api

//-- src/utils/api.js

import Axios from 'axios';
import qs from 'qs';
import store from '../store/'  //-- src/store/index.js
import Vue from 'vue'
import router from '../router/'
import myCookie from './cookie'    
//-- src/utils/cookie.js   這裡使用的是mdn網站的cookie封裝,附上鍊接
// https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie/Simple_document.cookie_framework

//create方法會建立一個新的axios例項,並繼承axios幾乎所有屬性,配置和方法
//建議使用 create 方法封裝 api ,不對 axios 本身做特殊配置
const api = Axios.create({
    //傳參是一個物件,配置包含baseUrl,timeout等等,既可以在這裡傳進去,也可以在例項化後設定配置
    
    baseURL: process.env.NODE_ENV==="development"?'/api':'https://example.domain',
    // 設定通用url,使用請求的方法時,就可以省略這個url
    // 可以通過process.env.NODE_ENV判斷是否開發環境,來決定是否使用代理url
    // 代理是用來解決跨域的。。
    
    headers: {
        //這個配置不用說了吧。預設攜帶請求頭,
        //有的後臺更喜歡讓前端把session_id放在header裡傳送
    },
    
    transformRequest: [function (data, headers) {
        // 此處是格式化發請求時,需要傳送的資料格式
        // 某些後臺在處理資料時不識別預設的 payload
        // 此處用第三方模組qs 轉換成 相容較好的 form-data(x-www-form-urlencoded)
        return qs.stringify(data);
    }],
    
    timeout: 10000,//超時,超出這時間,就會Promise.reject(),單位ms
    
    withCredentials: true,
    // 允許瀏覽器端在發請求時,攜帶cookie一起傳送,
    // 某些後臺語言會把session_id放在cookie裡返回給前端
    // 如果這時不允許傳送cookie的話  那麼後臺會判斷是另一個瀏覽器在登入操作。
    // 我都是在例項化後 配置這個屬性 因為我偶爾出現在這裡傳參配置會無效。
    // 設定這個屬性為true後 ,後臺不可以把允許跨域設定為'*',必須指定ip或域名
    
    proxy:{}//axios的代理我沒用過,不過看了下原始碼,也是開發環境才可以使用
});

//現在這裡再給例項化後的api 配置攔截  不是axios本身
//新增請求攔截器
api.interceptors.request.use(config => {
  //在傳送請求之前做某事,比如說 設定loading動畫顯示
  store.commit('loadingStart');
  //此處我是把全域性loading動畫的控制放在vuex的,所以引入了store
  return config
}, error => {
  //請求錯誤時做些事
  return Promise.reject(error)
});
//新增響應攔截器
api.interceptors.response.use(response => {
  //對響應資料做些事,比如說把loading動畫關掉
  store.commit('loadingOver');
  return response.data
  //此處我直接返回res.data,方不方便你們應該有點b數的
}, error => {
  //請求錯誤時做些事
  // error.response.status是後臺響應請求的狀態碼
  // 可以根據自己專案的需求  執行操作
  if (error.response.status===401){
  //這裡引入vue是因為要使用掛載在vue原型上的element-ui的彈窗元件
  // 因為這時掛載vue原型上的方法,這個import的vue並沒有例項化,無法省略prototype
    Vue.prototype.$alert(error.response.data.msg,{
      type:error.response.data.type,
      title:'Message',
    }).then(()=>{
      myCookie.removeItem('user');
      router.push({name:'SignIn'});//這裡就是引入router的目的
      console.clear();
    })
  }
  store.commit('loadingOver');//關閉動畫
  return Promise.reject(error)
});

api.defaults.withCredentials = true;
// 允許傳送cookie,根據自己的專案需求是否需要開啟
// axios的更多配置可以看官方的文件 github的 readme.md
// https://github.com/axios/axios   

export default api
複製程式碼

至此,一個簡單常用的api的配置部分封裝完成

這裡提一下proxy的使用,好多人出過錯。

// https://github.com/chimurai/http-proxy-middleware
'/api':{
    target:'http://example.domain',
    changeOrigin: true, //是否需要虛擬站點,我也不懂啥意思。。好像寫不寫差不多
    pathRewrite:{
        //這個屬性,是看後臺介面的請求url中是否有你配置的這個api
        //如果有的話你就不用寫這個屬性,
        //如果沒有,你就需要寫
        '^api':'',//把你本地請求的url中的api欄位去掉
    }
}

//以上的配置效果是
//--  /api/xxx --->>>  http://example.domain/xxx
// 如果不寫pathRewrite
//--  /api/xxx --->>>  http://example.domain/api/xxx
複製程式碼

當專案中需要多個url時,再建一個檔案,再create一個例項就可以,使用哪個就引入哪個,分開維護,很方便。

Service

這裡我們封裝完api的url和請求配置部分,下面就要封裝具體的請求。這裡我定義為services

//-- src/services/user.js  這裡可以把請求整理成模組,如果請求介面較多,後期維護方便

import api from '../utils/api'

// 引數是es6的解構,小白自己去看 阮老師 的 es6 
// USER_REGISTER宣告為一個方法
export const USER_REGISTER = ({user,password,email,phone,code}) => {
  return api.post('/register',{//此處 post '/register' ===>>  baseURL+'/register'
    user,password,email,phone,code
    // es6 物件的鍵值對字元相同時的寫法
  })
};//註冊介面

export const USER_LOGIN = ({user,password}) => {
  return api.post('/signIn',{user,password});
};//登入介面

// 我這裡匯出的是多個方法名的集合,是一個物件,
// 可以通過 import { USER_LOGIN,USER_REGISTER } from '...' 使用
// 這裡就舉例 2個  不詳細寫了
複製程式碼

這裡提一下,很多小夥伴對axios的使用文件不熟悉,在get和post時,經常出現2個方法的混餚,傳參出錯,建議對不熟悉語法的,不要使用‘省略式’的寫法,下面可以看一下詳細寫法,可以減少出錯率。(其實就是官方文件的。)

axios({
    url:'...',
    method:'...',
    headers:{},
    params:{},//這個是get攜帶請求引數用的,拼接在url的,直接寫在url上也可以,
    data:{},// 這個是post put patch請求傳送的資料
})
複製程式碼

Vuex

現在我們已經封裝完成完整的api了,如果覺得沒必要使用vuex,可以直接引入servicesuser.js直接使用了。

import { USER_LOGIN } from '@/services/user'

USER_LOGIN({user,password}).then().catch()
複製程式碼

為什麼要用加入vuex,vuex的action是用來處理非同步操作的,同時vuex的state是整個專案元件之間資料流動的重要環節,多數資料會在元件之間通用,將api請求放在action中可以更合理的讀寫state,不多說,上程式碼。

個人習慣,一用vuex就喜歡直接使用模組(namespaced),這裡推薦vuex多使用模組,在團隊開發中提供很大的便利,右轉vuex官網

//-- src/store/modules/user/action.js
import { USER_INFO } from '@/services/user'

export default {
    async getInfo({ commit }){
        try{ //請求成功
            const data = await USER_INFO();
            commit('setUserInfo',data);
            //模組的action中使用commit,不需要使用namespace字首
            return data
            // async函式的返回值,預設會以promise.resolve(data)的形式
        }catch(e){
        //請求失敗或者程式碼塊出錯
            return e
        }
        
    }
}
//合理使用 async/await 可以讓邏輯程式碼更清晰
複製程式碼
//--- src/store/modules/user/commit.js
export default {
    setUserInfo(state,data){
        state.info = data
    }
}
複製程式碼
<!-- 此處假設已經呼叫getInfo請求 -->
<template>
    <div>
        <p>{{info.name}}</p>
        <p>{{info.age}}</p>
        <img :src="info.photo" />
    </div>
</template>
<script>
import { mapState } from 'vuex'
export default{
    //...
    computed:{
        //需要使用模組中資料時,第一個引數為模組名
        ...mapState('user',{
            info:'info'
        })
    }
}
</script>
複製程式碼

tips:懂得分享才能走的更遠。

歡迎來我的Vue技術群交流:Vue887516034

如果覺得對你有用,就打賞一下吧。

axios封裝api axios封裝api

相關文章