Vue全家桶-前后端交互
一、什么是前后端交互模式
1、接口调用方式
- 原生ajax
- 基于Jquery的ajax
- fetch
- axios
二、Promise的概念和用法
1、异步调用
异步效果分析
- 定时任务
- Ajax
- 事件函数
多次异步调用的依赖分析
- 多次异步调用的结果顺序不确定
- 异步调用结果如果存在依赖关系的话需要嵌套(回调地狱)
2、Promise概述
Promise是异步编程的一种解决方案,从语法上讲,Promise是一个对象,从它可以获取异步操作的消息(解决回调地狱)
使用Promise主要的好处
- 可以避免多层异步调用嵌套问题(回调地狱)
- Promise对象提供了简洁的API,使得控制异步操作更加容易
3、Promise基本用法
- 实例化Promise对象,构造函数中传递函数,该函数中用于处理异步任务
- resolve和reject两个参数用于处理成功和失败两种情况,并通过获取处理结果
<script type="text/javascript">
/**
* Promise基本使用
*/
var p = new Promise(function(resolve,reject){
//这里用于实现异步任务(Ajax)
setTimeout(function(){
var flag = false;
if(flag){
//正常情况
resolve('hello');
}else{
//异常情况
reject('error');
}
},100)
});
p.then(function(data){
console.log(data);
},function(info){
console.log(info)
});
</script>
4、then参数中的函数返回值
- 返回Promise实例对象
返回的该实例对象会调用下一个then
- 返回普通值
返回的普通值会直接传递给下一个then,通过then参数中函数的参数接受该值
<script type="text/javascript">
/*
then参数中的函数返回值
*/
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){
return queryData('http://localhost:3000/data1');
})
.then(function(data){
return new Promise(function(resolve, reject){
setTimeout(function(){
//resolve(123);
reject("error");
},1000)
});
})
.then(function(data){
return 'hello';
})
.then(function(data){
console.log(data)
})
</script>
Promise常用API
实例方法
- p.then( ) 得到异步任务的正确结果
- p.catch( ) 获取异常信息
- p.finally( ) 成功与否都会执行
<script type="text/javascript">
/**
* Promise常用API-实例方法
*/
function foo(){
return new Promise(function(resolve,reject){
//定义一个异步定时任务
setTimeout(function(){
resolve(123);
},1000);
})
}
foo() //正常
.then(function(data){
console.log(data)
}) //错误
.catch(function(data){
console.log(data)
}) //结束
.finally(function(){
console.log("finished")
});
# 这种写法也是ok的
foo()
.then(function(data){
console.log(data)
},function(data){
console.log(data)
})
.finally(function(){
console.log("finished")
})
</script>
对象方法
- Promise.all( ) 并发处理多个异步任务,所有任务执行完成才能得到结果
- Promise.race( ) 并发处理多个任务,只要有一个任务完成就能得到结果
注意:这里的result是一个数组,里面的数据跟p1,p2,p3中传递过来的一一对应
<script type="text/javascript">
/**
* Promise常用API-对象方法
*/
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);
});
}
var p1 = queryData('http://localhost:8080/a1');
var p2 = queryData('http://localhost:8080/a2');
var p3 = queryData('http://localhost:8080/a3');
//并发执行(全部执行完才执行)
Promise.all([p1,p2,p3]).then(function(result){
console.log(result)
})
//(zhiyao)
Promise.race([p1,p2,p3]).then(function(result){
console.log(result)
})
</script>
三、fetch的使用
1、fetch概述
更简单的数据获取方式,功能更前大、更灵活,可以看做是xhr的升级版,他还是基于Promise实现的
语法结构
2、基本用法
实例
<script type="text/javascript">
/**
* Fetch API基本用法
*/
fetch('http://112.74.167.52:8089/channel/createOrder').then(function(data){
//这里的text()方法属于fetch API一部分,它返回一个Promise对象,用于获取后台返回的数据
return data.text();
}).then(function(data){
console.log(data)
})
</script>
3、fetch请求参数
常用配置选项
-
method(String) ,http请求方式:GET、POST、PUT、DELETE,默认是GET
-
body(String) ,请求体
-
headers(Object) ,请求头的设置
GET方法传递参数
POST方法传参
正常开发中,使用application/json格式居多
实例
<script type="text/javascript">
/**
* Fetch 请求参数
*/
fetch('http://112.74.167.52:8089/channel/createOrder',{
method:'post',
body:JSON.stringify({
'orderCode':'AS202012010001CN'
}),
headers:{
'Content-Type':'application/json'
}
})
.then(data => {
return data.text();
})
.then(data =>{
console.log(data);
})
</script>
4、fetch响应结果
响应数据格式
- text( ):将返回体处理成字符串类型
- json( ):返回结果和JSON.parse(responseText)一样
<script type="text/javascript">
/**
* Fetch 请求参数
*/
fetch('http://112.74.167.52:8089/channel/createOrder',{
method:'post',
body:JSON.stringify({
'orderCode':'AS202012010001CN'
}),
headers:{
'Content-Type':'application/json'
}
})
.then(data => {
//将数据转为json格式
//return data.text();
return data.json();
})
.then(ret =>{
//var json = JONS.parse(ret);
//console.log(json.msg)
//这样就可以取出具体的值
console.log(ret.msg);
console.log(ret.resCode);
console.log(ret.obj);
})
</script>
四、axios的使用
axios是一个基于Promise用于浏览器和node.js的和HTTP客户端
1、基本特性
- 支持浏览器和node.js
- 支持promise
- 能拦截请求和响应
- 自动转换JSON数据
2、axios的基本用法
简单示例
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="js/axios.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
<script type="text/javascript">
/**
* axios的基本使用
* 发送get请求
*/
axios.get('http://112.74.167.52:8089/channel/get').then(ret => {
//这里的data是axios的固定属性,用于获取后台返回的实际数据
console.log(ret.data);
})
</script>
</body>
</html>
3、axios的常用API
- get
- post
- put
- delete
get传递参数
- 通过url传递参数
- 通过params选项传递参数
//拼接的方式
axios.get('http://112.74.167.52:8089/channel/get?id=1').then(ret => {
//这里的data是axios的固定属性,用于获取后台返回的实际数据
console.log(ret.data);
})
//restful形式的请求
axios.get('http://112.74.167.52:8089/channel/get/1').then(ret => {
//这里的data是axios的固定属性,用于获取后台返回的实际数据
console.log(ret.data);
})
// 带对象的参数:params
axios.get('http://112.74.167.52:8089/channel/get',{
params:{
id:123,
name:'pihao'
}
}).then(ret => {
console.log(ret.data);
})
post请求传递参数
post请求,传递json格式的数据
/**
* axios
* post请求,请求体为对象 json格式的数据
*/
axios.post('http://112.74.167.52:8089/channel/createOrder',{
orderCode:'AS202012030001CN'
}).then(ret => {
console.log(ret.data);
})
post请求,表单格式的数据
4、axios的响应结果
响应的主要属性
- data:实际响应回来的数据
- headers:响应头信息
- status:响应状态码
- statusText:响应状态信息
5、axios全局配置
- axios.defaults.timeout = 3000 超时时间
- axios.defaults.baseURL=‘http://localhost:8080’/app 默认地址
- axios.defaults.headers[‘token’] = ‘abc123’ 默认请求头
实例
//url
axios.defaults.baseURL='http://112.74.167.52:8089/channel/';
//timeout
axios.defaults.timeout=3000;
//headers
axios.defaults.headers['mytoken']='hello';
axios.post('createOrder',{
orderCode:'AS202012030001CN'
}).then(ret => {
console.log(ret.data);
})
6、axios拦截器
请求拦截器
拦截器实例
<script type="text/javascript">
/**
* axios请求拦截器
*/
axios.interceptors.request.use(function(config){
//配置请求头
config.headers.mytoken = 'pihao';
//请求的其他配置。。。
//还可以通过config.url获取请求地址,然后对请求地址做相应配置
return config;
},function(err){
console.log(err);
})
//测试请求
axios.get('http://112.74.167.52:8089/channel/get').then(ret=>{
console.log(ret.data);
});
</script>
响应拦截器
拦截器实例
/**
* axios响应拦截器
*/
axios.interceptors.response.use(function(res){
//这里的res就是那个还没处理的响应数据
//这里做一个小小的处理
var data = res.data;
//直接返回json数据
return data;
},function(err){
console.log(err);
})
//测试请求
axios.get('http://112.74.167.52:8089/channel/get').then(ret=>{
//注意:这里没有 .data
console.log(ret);
});
五、async/await方式调用接口
1、async/await的基本用法
- async/await是ES7引入的新语法,可以更加方便的进行异步操作
- async关键字用于函数上(async函数的返回值是Promise实例对象)
- await关键字用于async函数当中(await可以得到异步的结果)
语法
实例
<script type="text/javascript">
/**
* async/await 处理异步操作
*/
axios.defaults.baseURL='http://112.74.167.52:8089/channel/'
//async处理异步任务,返回的其实一个Promise对象,这个对象里面的传的参数的响应的结果
async function queryData(){
var res = await axios.get('get');
return res.data;
}
//通过then处理Promise
queryData().then(data => {
console.log(data); //已经处理过了的json响应数据
})
</script>
什么?上个例子懵逼?,那么看看下面这个简历的例子
<script type="text/javascript">
/**
* async/await 处理异步操作
*/
async function queryData(){
var res = await new Promise(function(resolve,reject){
setInterval(function(){
resolve('async test');
},2000);
})
return res;
}
//通过then处理Promise
queryData().then(data => {
console.log(data); //拿到处理的结果 async test
})
</script>
2、async/await 处理对个异步请求
第一个请求的结果传递给第二个请求的参数
实例
<script type="text/javascript">
/**
* async、await处理多个异步任务
*/
axios.defaults.baseURL = 'http://112.74.167.52:8089/channel/';
async function queryData(){
var info = await axios.get('get');
//将上个请求的返回值作为参数传递给第二个请求
var res = await axios.post('createOrder',info.data);
return res.data;
};
queryData().then(res =>{
console.log(res);
})
</script>
六、基于后台接口实现案例
自己用springboot写的java后台,配合前端vue使用
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="js/vue.js" type="text/javascript" charset="utf-8"></script>
<script src="js/axios.js" type="text/javascript" charset="utf-8"></script>
<style type="text/css">
.grid{
margin: auto;
width: 500px;
text-align: center;
}
.grid table{
width: 100%;
border-collapse: collapse;
}
.grid th,td{
padding: 10;
border: 1px dashed orange;
height: 35px;
line-height: 35px;
}
.grid th{
background-color: orange;
}
.grid .book{
padding-bottom: 10px;
padding-top: 5px;
background-color: #F3DCAB;
}
.grid .total{
height: 30px;
line-height: 30px;
background-color: hotpink;
}
</style>
</head>
<body>
<div id="app">
<div class="grid">
<div>
<h1>图书管理</h1>
<div class="book">
<div>
<label for="id">
编号:
</label>
<input type="text" id="id" v-model="id" style="width: 30%" :disabled="flag" v-focus/>
<label for="name">
名称:
</label>
<input type="text" id="name" v-model="name" style="width: 30%"/>
<!-- 这里的禁用是为了输入相同的书名的时候,不允许添加 -->
<button type="button" @click="handel" :disabled="btnFlag">提交</button>
</div>
</div>
</div>
<div class="total">
<span>总数:{{sum}}</span>
</div>
<table >
<thead>
<tr>
<th>编号</th>
<th>名称</th>
<th>时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr :key='item.id' v-for='item in books'>
<td>{{item.id}}</td>
<td>{{item.name}}</td>
<td>{{item.date}}</td>
<td>
<a href="#" @click.prevent="toEdit(item.id)">修改</a>
<span>|</span>
<a href="#" @click.prevent="deleteBook(item.id)">删除</a>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<script type="text/javascript">
//设置axios的基准路径
axios.defaults.baseURL='http://112.74.167.52:8089/';
axios.interceptors.response.use(function(res){
return res.data;
},function(error){
console.log(error);
})
var vm = new Vue({
el: '#app',
data:{
id:'',
name:'',
flag:false,
btnFlag:false,
books:[]
},
computed:{
sum:function(){
//计算图书的总数
return this.books.length;
}
},
methods:{
handel: async function(){
//该方法其实可以与编辑图书重用
if(this.flag){
//编辑图书
var res = await axios.put('/books/'+this.id,{
name : this.name
});
if(res == 'success'){
//重新加载列表
this.queryData();
}
this.flag = false;
}else{
//添加图书
entity = {
name:this.name,
id:this.id
};
var result = await axios.post('books',entity);
if(result == 'success'){
this.queryData();
}
}
this.id = '';
this.name = '';
},
toEdit: async function(id){
//编辑的时候禁止修改id
this.flag = true;
//根据id找到相应的数据
var book = await axios.get('/books/'+id);
//找到数据之后填充到页面上
this.id = book.id;
this.name = book.name;
},
deleteBook: async function(id){
//删除图书
var res = await axios.delete('/books/'+id);
if(res == 'success'){
//重新加载数据
this.queryData();
}
},
//加载图书列表
queryData:async function(){
this.books = await axios.get('books');
}
},
filters:{
format:function(val,pattern){
if(pattern == 'yyyy-MM-dd'){
return val.getFullYear()+"-"+(val.getMonth()+1)+"-"+val.getDate();
}
}
},
directives:{
focus:{
inserted:function(el){
el.focus()
}
}
},
watch:{
name:async function(val){
//验证图书名称是否已经存在
var result = await axios.get('/books/book?name='+this.name);
// console.log(result); //true or false
if(result){
//图书名称已经存在
this.btnFlag = true;
}else{
this.btnFlag = false;
}
}
},
mounted:function(){
//该生命周期钩子函数被触发的时候,模板已经可以使用了
//一般此时用于调用接口获取后台数据,然后把数据填充到模板
// axios.get('books').then(res => {
// this.books = res.data;
// })
//上面的方法不够简洁,我们可以在方法中封装函数queryData()
this.queryData();
}
});
</script>
</body>
</html>