- 前后端交互模式
- promise用法
- 接口調用-fetch用法
- 接口調用-axios用法
- 接口調用-async/await用法
- 基於接口的案例
1. 前后端交互模式
① 接口調用方式
- 原生Ajax
- 基於jQuery的Ajax
- fetch
- axios
jQuery中的Ajax側重點是DOM操作,而vue開發很少涉及到DOM操作,所以使用fetch和axios調用接口。
② URL地址格式
1)傳統形式的URL:
schema://host:port/path?query#fragment
- schema:協議。例如http、https、ftp等
- host:域名或者IP地址
- port:端口,http默認端口是80,可以省略
- path:路徑,例如/abc/a/b/c
- query:查詢參數,例如uname=lisi&age=12
- fragment:錨點(哈希hash),用於定位頁面的某個位置
例如下面都是符合規則的URL格式:
- http://www.test.com
- http://www.test.com/java/web
- http://www.test.com/java/web?flag=1
- http://www.test.com/java/web?flag=1#function
2)Restful形式的URL
HTTP請求方式:
- GET:查詢
- POST:添加
- PUT:修改
- DELETE:刪除
例如下面都是符合規則的URL地址:
- GET:http://www.hello.com/books
- POST:http://www.hello.com/books
- PUT:http://www.hello.com/books/123
- DELETE:http://www.hello.com/books/123
2. promise用法
① 異步調用
- 異步效果分析:
- 定時任務
- Ajax
- 事件函數
- 多次異步調用的依賴分析:
- 多次異步調用的結果順序不確定
- 異步調用結果如果存在依賴,則需要嵌套
// 多層嵌套會陷入回調地獄
$.ajax({ success: function(data) { if (data. status == 200) { $.ajax ({ success: function (data) { if (data. status == 200) { $.ajax({ success: function(data) { if (data. status == 200) {} } }); } }); } } });
② Promise概述
Promise是異步編程的一種解決方案,從語法上講,Promise是一個對象,從它可以獲取異步操作的消息。
使用Promise主要有以下好處:
- 可以避免多層異步調用嵌套問題(回調地獄)
- Promise對象提供了簡潔的API,使得控制異步操作更加容易
③ Promise基本用法
- 實例化Promise對象,構造函數中傳遞函數,該函數中用於處理異步任務
- resolve和reject兩個參數用於處理成功和失敗兩種情況,並通過p.then獲取處理結果
實際上就是把之前用到的回調函數用then的方式給重構了,這樣的話代碼就不會有多層嵌套了,而是變成線性結構。
var p = new Promise (function (resolve, reject) { //成功時調用resolve () //失敗時調用reject () }); p.then( funciton (ret) { //從resolve得到正常結果 },function(ret) { //從reject得到錯誤信息 });
例如下面這段測試代碼:
var p = new Promise(function (resolve, reject) { //這里用於實現異步任務 setTimeout(function() { var flag = false; if (flag) { // 正常情況 resolve('hello'); }else { // 異常情況 reject('出錯了'); } }, 100); }); p.then( funciton (data) { //從resolve得到正常結果 console.log(data); },function(info) { //從reject得到錯誤信息 console.log(info); });
④ 基於Promise處理Ajax請求
1)處理原生Ajax
function queryData(url) { return new Promise (function(resolve, reject) { var xhr = new XMLHttpRequest () ; xhr.onreadystatechange = function() { if (xhr.readyState != 4) return; if (xhr.readyState == 4 && xhr.status == 200) { // 處理正常的情況 resolve(xhr.responseText); }else{ // 處理異常情況 reject('出錯了'); } } xhr.open('get', url); xhr.send(null); }); } queryData('http://localhost:3000/data') .then(function(data){ console.log(data); },function(info){ console.log(info); });
2)發送多次Ajax請求
// 發送多個Ajax請求並且要保證順序 queryData('http://localhost:3000/data') .then(function(data){ console.log(data); return queryData('http://localhost:3000/data1'); }) // 這個then是上面return的queryData的結果調用的 .then(function(data){ console.log(data); return queryData('http://localhost:3000/data2'); }) .then(function(data){ console.log(data); });
注意:return的是一個新的promise實例對象,下一個then調用者就是上面return出來的promise對象,並且then當中的函數的參數data用於接收上一個異步任務的處理結果。
⑤ Promise常用的API
1)實例方法
- p.then()得到異步任務的正確結果
- p.catch()獲取異常信息
- p.finally()成功與否都會執行(尚且不是正式標准)
例如下面這段測試代碼:
function queryData() { return new Promise(function(resolve, reject){ setTimeout(function(){ // resolve(123); reject(' error'); }, 100); }) } foo() .then(function(data){ console.log(data) }) .catch(function(data){ console.log(data) }) .finally(function(){ console.log('finished') });
可以把兩個函數都傳遞給then,也可以then接受一個函數,另外一個通過catch來處理。catch的作用就是專門用來處理異常信息的。
2)對象方法
all和race都是對象方法,prototype里面的都是實例方法。
- Promise.all() 並發處理多個異步任務,所有任務都執行完成才能得到結果
- Promise.race() 並發處理多個異步任務,只要有一個任務完成就能得到結果
var p1 = queryData('http://localhost:3000/a1'); var p2 = queryData('http://localhost:3000/a2'); var p3 = queryData('http://localhost:3000/a3'); Promise.all([p1,p2,p3]).then(function(result){ console.log(result) }) Promise.race([p1,p2,p3]).then(function(result){ console.log(result) })
all()方法輸出的是一個數組,數組中數據的順序與promise實例對象的順序是一一對應的。all()方法可以用於發送多個請求的,並且請求之間沒有嵌套關系。
race()方法得到的是最開始返回的一個結果,另外兩個結果也已經返回回來了,但是我們並不關心。
3. 接口調用-fetch用法
① fetch概述
1)基本特性
- 更加簡單的數據獲取方式,功能更強大、更靈活,可以看作是xhr的升級版
- 基於Promise實現
2)語法結構
fetch(url).then(fn2) .then(fn3) ... .catch(fn)
官網:https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API
② fetch的基本用法
fetch('http://localhost:3000/fdata').then(data => { // text()方法屬於fetchAPI的一部分,它返回一個Promise實例對象,用於獲取后台返回的數據 return data.text(); }).then(function(data){ // 注意這里得到的才是最終的數據 console.log(data); });
注意:這里面的data不能直接拿到實際的數據,而是要通過fetch中API:data.text()來獲得數據,但是text()返回的是一個promise實例對象,所以需要把它return出去,然后通過下一個then來得到最終的數據。
③ fetch請求參數
1)常用配置選項
- method(String):HTTP請求方法,默認為GET(GET、POST、PUT、DELETE)
- body(String):HTTP的請求參數
- headers(Object):HTTP的請求頭,默認為{}
fetch('/abc', { method: 'get' }).then(data => { return data.text(); }).then(ret => { // 注意這里得到的才是最終的數據 console.log(ret); });
2)GET請求方式的參數傳遞
fetch('/abc?id=123', { method: 'get' }).then(data=>{ return data.text(); }).then(ret => { //注意這里得到的才是最終的數據 console.log(ret); });
Restful風格的API:
fetch('/abc/456'), { method: 'get' }).then(data => { return data.text(); }).then(ret => { //注意這里得到的才是最終的數據 console.log(ret); });
后台接口:
app.get('/abc', (req, res) => { res.send('傳統的URL傳遞參數!' + req.query.id); }); app.get('/abc/:id', (req, res) => { res.send('Restful形式的URL傳遞參數!' + req.params.id); });
3)DELETE請求方式的參數傳遞
fetch('/abc/123', { method: 'delete' }).then(data => { return data.text(); }).then(ret => { //注意這里得到的才是最終的數據 console.log(ret); });
app.delete('/abc/:id', (req, res) => { res.send('DELETE請求傳遞參數!' + req.params.id); });
4)POST請求方式的參數傳遞
- 傳遞拼接字符串格式的數據:
fetch('/books', { method: 'post', body: 'uname=lisi&pwd=123', headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }).then(data => { return data.text(); }).then(ret => { console.log(ret); });
請求頭是必須要設置的,否則內容傳遞不過去。
后台必須要使用第三方模塊body-parser來接收post發送過來的數據
// 處理post請求中的json格式的數據 app.use(bodyParser.json()); // 處理post請求中的字符串格式的數據 app.use(bodyParser.urlencoded({extended: false}));
app.post('/books', (req, res) => { res.send('POST請求傳遞參數!' + req.body.umane + '---' + req.body.pwd); })
- 傳遞json格式的數據:
fetch('/books', { method: 'post', body: JSON.stringify({ uname: 'lisi', age: 12 }), headers: { 'Content-Type': 'application/json' } }).then(data => { return data.text(); }).then(ret => { console.log(ret); });
后台接口還是和上面一樣,沒有變化。
5)PUT請求方式的參數傳遞
fetch('/books/123', { method: 'put', body: JSON.stringify({ uname: 'lisi', age: 12 }), headers: { 'Content-Type': 'application/json' } }).then(data => { return data.text(); }).then(ret => { console.log(ret); });
app.put('/books/:id', (req, res) => { res.send('PUT請求傳遞參數!' + req.params.id+ '---' + req.body.uname + '---' + req.body.pwd); })
④ fetch響應結果
響應數據格式:
- text():將返回體處理成字符串類型
- json():返回結果和JSON.parse(responseText)一樣
后台返回json格式的數據:
app.get('/json', (req, res) => { res.json({ uname: 'lisi', age: 13, gender: 'male' }); });
客戶端接收到后台傳遞過來的數據:
fetch('/json').then(data => { // json()方法返回的就是json格式的數據 return data.json(); }).then(ret => { //注意這里得到的才是最終的數據 console.log(ret); }); // 類似於下面這步: fetch('/json').then(data => { return data.text(); }).then(ret => { //還需要自己將數據轉換為json格式 var obj = JSON.parse(data); console.log(obj.uname, obj.age, obj.gender); });
4. 接口調用-axios用法
① axios的基本特性
axios是一個基於Promise用於瀏覽器和node.js的HTTP客戶端。
官網:https://github.com/axios/axios
它具有以下特征:
- 支持瀏覽器和node.js
- 支持Promise
- 能攔截請求和相應
- 自動轉換JSON數據
axios是一個專門的第三方的JS庫,用來實現接口的調用。
② axios的基本用法
axios.get('/adata') .then(ret => { // data屬性名稱是固定的,用於獲取后台響應的數據 console.log(ret.data); })
注意:ret是形參,並不是我們實際需要得數據,而是要通過ret.data才能獲得我們最終想要的數據,其中data的名字是固定的
③ axios常用的API
- get:查詢數據
- post:添加數據
- put:修改數據
- delete:刪除數據
④ axios的參數傳遞
1)GET傳遞參數
- 通過URL傳遞參數
- 通過params選項傳遞參數
方法一:
axios.get('/adata?id=123') .then(ret => { console.log(ret.data); })
方法二:
axios.get('/adata/123') .then(ret => { console.log(ret.data); })
方法三:
axios.get('/adata', { params: { id: 123 } }) .then(ret => { console.log(ret.data); })
后台接收數據:
app.get('/adata', (req, res) => { res.send('axios get 傳遞參數' + req.query.id); }); app.get('/axios/:id', (req, res) => { res.send('axios get (Restful)傳遞參數' + req.params.id); });
注意:前端如果是用params傳參(例如方法三),則后台接口調用的還是req.query,而不是req.params
2)DELETE傳遞參數
- 參數傳遞方式與GET類似
方法一:
axios.delete('/adata?id=123') .then(ret => { console.log(ret.data); })
方法二:
axios.delete('/adata/123') .then(ret => { console.log(ret.data); })
方法三:
axios.delete('/adata', { params: { id: 123 } }) .then(ret => { console.log(ret.data); })
3)POST傳遞參數
post請求的兩種傳參方式:
- 通過選項傳遞參數(默認傳遞的是json格式的數據)
axios.post('/adata', { uname: 'tom', pwd: 123 }).then(ret => { console.log(ret.data); })
- 通過URLSearchParams傳遞參數(application/x-www-form-urlencoded)
const params = new URLSearchParams(); params.append('uname', 'zhangsan'); params.append('pwd', '123'); axios.post('/adata', params).then(ret => { console.log(ret.data); })
兩種方式的后台接口都一樣:
app.post('/adata', (req, res) => { res.send('axios post 傳遞參數' + req.body.uname + '---' + req.body.pwd); })
4)PUT傳遞參數
- 參數傳遞方式與POST類似
axios.put('/adata/123', { uname: 'tom', pwd: 123 }).then(ret => { console.log(ret.data); })
app.put('/adata/:id', (req, res) => { res.send('axios put 傳遞參數' + req.params.id + req.body.uname + '---' + req.body.pwd); })
⑤ axios的響應結果
響應結果的主要屬性:
- data:實際響應回來的數據
- headers:響應頭信息
- status:響應狀態碼
- statusText:響應狀態信息
axios.post('/axios-json').then(ret => {
console.log(ret);
})
axios中對於json響應數據來說,返回來的數據不需要我們自己再做轉化了,axios內部已經處理好了,直接可以當對象來使用。例如:ret.data.username
⑥ axios的全局配置
axios.defaults.timeout = 3000; // 超時時間 axios.defaults.baseURL = 'http://localhost:3000/app'; // 默認地址 axios.defaults.headers['mytoken'] = 'aqwerwqwer2ewrwe23eresdf23' // 設置請求頭
在設置完基准路徑的時候,在實際調用接口的時候就可以簡化操作,請求發出去的時候實際上會自動拼接上基准路徑。
對於跨域來說,請求頭是需要后台來進行配置的:
// 允許請求頭為mytoken的傳遞 res.header('Access-Control-Allow-Headers', 'mytoken');
⑦ axios攔截器
1)請求攔截器:在請求發出之前設置一些信息。
// 添加一個請求攔截器 axios.interceptors.request.use (function(config) { console.log(config.url); config.headers.mytoken = 'nihao'; //在請求發出之前進行些信息設置 return config; }, function (err) { //處理響應的錯誤信息 console.log(err); });
注意:一定要把config return出去,否則是不生效的。
通過上面的攔截器可以控制所有的請求,這樣就給每個請求設置了一個響應頭:
axios.get('http://localhost:3000/adata').then(data => {
console.log(data);
});
上面的axios請求就有一個mytoken的請求頭:
2)響應攔截器:在獲取數據之前對數據做一些加工處理
// 添加一個響應攔截器 axios.interceptors.response.use (function(res) { console.log(res); var data = res.data; //在這里對返回的數據進行處理 return data; }, function (err) { //處理響應的錯誤信息 console.log(err); });
注意:形參res也不是我們需要的實際數據,而是axios所包裝的對象,通過對象中的data才能拿到實際的數據。
上面的響應攔截器中已經對res做了處理,所以返回的是我們實際需要的數據,所以在每個請求中不用再使用res.data來獲取實際需要的數據:
axios.get('http://localhost:3000/adata').then(data => { // 這個data不是axios對象,而是我們實際需要的數據 console.log(data); });
返回的不再是數據對象了,而是我們需要的數據。這樣以后我們調用任何接口,所有的then當中得到的數據都是我們實際需要的后台返回來的數據,不需要再通過.data的方式來獲取數據了。
5. 接口調用-async/await用法
雖然promise調用接口的方式相比於傳統的回調函數的方式還是方便很多,但promise也不是最好的辦法,比如說我們要發送多個異步接口的調用,要想保證它們的順序,得通過then的方式來進行鏈式的操作,這樣從代碼的層面還是不夠簡潔的。為了進一步的提高編程體驗,就誕生了一種新的語法async/await:
① async/await的基本用法
- async/await是ES7引入的新語法,可以更加方便地進行異步操作
- async關鍵字用於函數上(async函數地返回值是Promise實例對象)
- await關鍵字用於async函數當中(await可以得到異步的結果)
async function queryData(id) { const ret = await axios.get('/data'); return ret; } queryData.then(ret => { console.log(ret); });
注意:
- get請求參數返回來的是一個promise實例對象,在這個實例對象前面接上一個await,這樣就可以直接通過返回值來得到異步的結果了。不再需要then,也不再需要回調函數了。
- 這種函數的返回值也是一個promise實例對象,如果在async函數的內部直接把得到的異步結果再返回出去,那么這個函數在被調用的時候就可以通過then的方式來得到上面的返回值。
- await后面必須跟一個promise實例對象。
var ret = await new Promise(function(resolve, reject){/*...*/});
② async/await處理多個異步請求
// 設置默認的基准地址 axios.defaults.baseURL = 'http://localhost:3000'; async function queryData() { var info = await axios.get('async1'); var ret = await axios.get('async2?info=' + info.data); return ret.data; } queryData().then(function(data){ console.log(data); });
app.get('/async1', (req, res) => { res.send('hello'); }); app.get('/async2', (req, res) => { if(req.query.info == 'hello'){ res.send('world'); }else { res.send('error'); } });
await這種異步處理方式相當於之前的普通函數的調用,可以直接拿到結果,而不用擔心多個異步任務的執行順序問題。
6. 基於接口的案例