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'
根路徑
- baseURL: 'http://mpolaris.top:8080'
請求前的資料處理
- 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);
}