十. Axios網路請求封裝

MPolaris發表於2020-12-07

1. 網路模組的選擇

Vue中傳送網路請求有非常多的方式,那麼在開發中如何選擇呢?

選擇一:傳統的Ajax是基於XMLHttpRequest(XHR)

為什麼不用它呢?非常好解釋配置和呼叫方式等非常混亂,編碼起來看起來就非常蛋疼。所以真實開發中很少直接使用而是使用jQuery-Ajax。

選擇二:使用jQuery-Ajax

相對於傳統的Ajax非常好用,為什麼不選擇它呢?首先我們先明確一點,在Vue的整個開發中都是不需要使用jQuery了,那麼就意味著為了方便我們進行一個網路請求特意引用一個jQuery,你覺得合理嗎?jQuery的程式碼1w+行,Vue的程式碼才1w+行,完全沒有必要為了用網路請求就引用這個重量級的框架。

選擇三:官方在Vue1.x的時候,推出了Vue-resource

Vue-resource的體積相對於jQuery小很多,另外Vue-resource是官方推出的。為什麼不選擇它呢?在Vue2.0退出後,Vue作者就在GitHub的Issues中說明了去掉vue-resource,並且以後也不會再更新。那麼意味著以後vue-reource不再支援新的版本也不會再繼續更新和維護,如果使用它對以後的專案開發和維護都存在很大的隱患。

選擇四: axios

在說明不再繼續更新和維護vue-resource的同時,作者還推薦了一個框架: axios。axios有非常多的優點並且用起來也非常方便,我們將對他詳細學習。

2. jsonp網路請求封裝

在前端開發中我們一種常見的網路請求方式就是JSONP,使用JSONP最主要的原因往往是為了解決跨域訪問的問題。

JSONP的原理是什麼呢?JSONP的核心在於通過<script>標籤的src來幫助我們請求資料,原因是我們的專案部署在domain1.com伺服器上時,是不能直接訪問domain2.com伺服器上的資料的。這個時候我們利用<script>標籤的src幫助我們去伺服器請求到資料,將資料當做一個javascript的函式來執行並且執行的過程中傳入我們需要的json。所以封裝jsonp的核心就在於我們監聽window上的jsonp進行回撥時的名稱。

JSONP如何封裝呢?我們一起自己來封裝一個處理JSONP的程式碼吧

function jsonp(options) {
    options = options || {};
    if (!options.url || !options.callback) {
        throw new Error('請傳入合法引數');
    }

    // 建立script標籤,並加入到頁面中
    // 返回的回撥函式名,加入隨機引數避免快取
    var callbackName = ('jsonp_' + Math.random()).replace('.', '');
    // 獲取head標籤
    var head = document.getElementsByTagName('head')[0];
    // 填充回撥函式名
    options.data[options.callback] = callbackName;
    // 格式化引數
    var paramas = formatParams(options.data);
    // 建立script標籤
    var script = document.createElement('script');
    // 插入script標籤的head
    head.appendChild(script);

    // 建立JSONP回撥函式
    // window[callbackName]的形式,可是的回撥函式可被全域性呼叫
    window[callbackName] = function(json) {
        // script標籤的哦src屬性只在第一次設定時起作用,即script標籤標籤是無法重用的,故每次建立回撥函式,即每次設定script標籤是需要將前一個script以及其src移除
        head.removeChild(script);
        clearTimeout(script.timer);
        window[callbackName] = null;
        options.success && options.success(json);
    };

    // 傳送請求

    script.src = options.url + '?' + paramas;
    // 超時處理
    if (options.timeout) {
        script.timer = setTimeout(function() {
            window[callbackName] = null;
            head.removeChild(script);
            options.fail && options.fail(message, '請求超時');
        }, timeout);
    }
}

//格式化引數
function formatParams(data) {
    var arr = [];
    for (var name in data) {
        arr.push(encodeURIComponent(name) + '=' + encodeURIComponent(data[i]));
    }
    return arr.join('&');
}

3. axios基本使用

3.1 為什麼選擇axios(ajax i/o system)

為什麼選擇axios? 作者推薦,功能特點

功能特點

  • 在瀏覽器中傳送 XML.HttpRequests 請求
  • 在 node.js 中傳送 http請求
  • 支援 Promise API
  • 攔截請求和響應
  • 轉換請求和響應資料
  • ...
3.2 請求方式

支援多種請求方式

  • axios(config)
  • axios.request(config)
  • axios.get(url[, config])
  • axios.delete(url[, config])
  • axios.head(url[, config])
  • axios.post(url[, data[, config]])
  • axios.put(url[, data[, config]])
  • axios.patch(url[, data[, config]])
3.3 傳送基本請求

安裝axios

npm install axios --save

前端配置跨域

//vue.comfig.js
module.exports = {
    devServer: {
        //配置跨域
        proxy: {  
            '/api': {  ///配置跨域,將所有帶有'/api'的請求都攔截,代理到target上
                target: 'http://mpolaris.top:8080', //目標ip地址
                ws: true,
                changOrigin: true,//允許跨域
                pathRewrite: {
                    '^/api': ''// 替換請求路徑中的'/api'字元
                }
            }
        }
    }
}

傳送get請求

<script>
import axios from 'axios'

export default {
  name: 'app',
  created() {
    //1.沒有請求引數
    axios.get('/api/portal/article/categories')
    .then(res => {
      console.log(res);
    }).catch(err => {
      console.log(err);
    })
    //2.有請求引數
    axios.get('/api/portal/article/label/',{
      params: {size: 3}
    }).then(res => {
      console.log(res);
    }).catch(err => {
      console.log(err);
    })
  }
}
</script>

傳送並行請求

有時候,我們可能需求同時傳送兩個請求,使用axios.all, 可以放入多個請求的陣列,axios.all([]) 返回的結果是一個陣列,使用 axios.spread 可將陣列 [res1,res2] 展開為 res1, res2

<script>
import axios from "axios";

export default {
  name: "app",
  created() {
    //傳送並行請求
    axios
      .all([
        axios.get("/api/portal/article/categories"),
        axios.get("/api/portal/article/label/", {
          params: { size: 3 },
        }),
      ])
      .then(
        axios.spread((res1, res2) => {
          console.log(res1);
          console.log(res2);
        })
      );
  }
};
</script>
3.4 全域性配置

在上面的示例中我們的 BaseURL 是固定的,事實上在開發中可能很多引數都是固定的,這個時候我們可以進行一些抽取,也可以利用axiox的全域性配置屬性 defaults

export default {
  name: "app",
  created() {
    //提取全域性的配置
    axios.defaults.baseURL = '/api';
	axios.defaults.timeout = 5000;
    axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
    //...
      
    axios
      .all([
        axios.get("/portal/article/categories"),
        axios.get("/portal/article/label/", {
          params: { size: 3 },
        }),
      ])
      .then(
        axios.spread((res1, res2) => {
          console.log(res1);
          console.log(res2);
        })
      );
  }
};
</script>
3.5 axios常見的配置項

請求地址

  • url: '/user'

請求型別

  • method: 'get'

根路徑

請求前的資料處理

  • transformRequest: [function(data){}]

請求後的資料處理

  • transformResponse: [function(data){}]

自定義的請求頭

  • headers: {'x-Requested-With': 'XMLHttpRequest'}

URL查詢物件

  • params: { id: 12 }

查詢物件序列化函式

  • paramsSerializer: function(params){ }

request body

  • data: { key: 'aa'}

超時設定

  • timeout: 1000,

跨域是否帶Token

  • withCredentials: false

自定義請求處理

  • adapter: function(resolve, reject, config){}

身份驗證資訊

  • auth: { uname: '', pwd: '12'}

響應的資料格式 json / blob /document /arraybuffer / text / stream

  • responseType: 'json'

4. axios的例項和模組封裝

4.1 axios例項

為什麼要建立axios的例項呢?

當我們從axios模組中匯入物件時,使用的例項是預設的例項(全域性axios)。當給該例項設定一些預設配置時這些配置就被固定下來了,但是後續開發中某些配置可能會不太一樣。比如某些請求需要使用特定的baseURL或者timeout或者content-Type等,這個時候我們就可以建立新的例項並且傳入屬於該例項的配置資訊。

//建立新的例項
const axiosInstance = axios.create({
   baseURL: '/api',
   timeout: 2000,
   headers: {
     'Content-Type': 'application/x-www-form-urlencoded'
   }
});
//傳送網路請求
axiosInstance({
   url: '/portal/article/categories',
   method: 'get'
}).then(res => {
   console.log(res);
})
4.2 axios模組封裝
//utils/httpRequest.js

import axios from 'axios'

//1.初步封裝,將結果或錯誤資訊通過函式形參回撥出去
// export function request(config,success,failure) {
//     //建立axios例項
//     const instance = axios.create({
//         baseURL: '/api',
//         timeout: 5000
//     });

//     instance(config).then(res => {
//         success(res);
//     }).catch(err => {
//         failure(err);
//     })
// }


// 2.改進:使用Promise封裝
// export function request (config) {
//     return new Promise((resolve, reject) => {
//         //建立axios例項
//         const instance = axios.create({
//             baseURL: '/api',
//             timeout: 5000
//         });

//         //傳送網路請求
//         instance(config).then(res => {
//             resolve(res);
//         }).catch(err => {
//             reject(err);
//         })
//     })
// }

// 3.改進:其實axios例項返回的就是一個Promise(看原始碼發現),所以
//      我們可以直接返回axios例項,在外面也可以直接調then和catch
export function request(config) {
    //建立axios例項
    const instance = axios.create({
        baseURL: '/api',
        timeout: 5000
    });

    //傳送網路請求
    return instance(config);
}
<script>
import { request } from "@/utils/httpRequest";

export default {
  name: "app",
  created() {
    // request(
    //   {
    //     url: "/portal/article/categories",
    //     method: "get",
    //   },res => {
    //     console.log(res);
    //   }, err => {
    //     console.log(err);
    //   }
    // );

    request({
      url: '/portal/article/categories',
      method: 'get'
    }).then(res => {
      console.log(res);
    }).catch(err => {
      console.log(err);

    })
  },
};
</script>
4.3 攔截器

axios提供了攔截器,用於我們在傳送每次請求或者得到相應後進行對應的處理。

給我們上面封裝的請求加上攔截器

//utils/httpRequest.js

import axios from 'axios'

export function request(config) {
    //建立axios例項
    const instance = axios.create({
        baseURL: '/api',
        timeout: 5000
    });

    //配置請求和響應攔截,注意直接寫axios就是全域性攔截
    instance.interceptors.request.use(config => {
        console.log('這裡是request攔截success中');
        return config
    }, err => {
        console.log('這裡是request攔截器failure中');
        return err
    })

    instance.interceptors.response.use(response => {
        console.log('這裡是response攔截success中');
        return response.data
    }, err => {
        console.log('這裡是response攔截器failure中');
        return err
    })

    //傳送網路請求
    return instance(config);
}

相關文章