Vue前后端交互模式
- 前后端交互模式
- 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. 基于接口的案例