1.接口调用方式
- 原生ajax;基于 jQuery 的ajax;fetch;axios;
1)JavaScript 的执行环境是单线程;异步模式可以执行多个任务;
- JS中常见的异步调用:定时任务;ajax;事件函数;
- 多次异步调用的结果,顺序不确定 (先执行其他函数,再执行异步);如果存在依赖需要,嵌套;回调地狱;
2)传统形式的 URL: http://www.baidu.com/java/web?flag=1#function
Restful 形式的 URL
http://www.hello.com/books GET
http://www.hello.com/books POST
http://www.hello.com/books/123 PUT
http://www.hello.com/books/123 DELETE
2. promise
1) promise 解决异步 深层嵌套问题(回调地狱)
//使用new构建一个 Promise实例,并传入一个函数,用于处理异步任务;函数传入两个参数:
//resolve 异步操作成功后,执行的回调函数;reject 异步操作失败后,执行的回调函数;
//并通过p.then获取处理结果;
var p = new Promise(function(resolve, reject){
// 这里用于实现异步任务;resolve reject这两个参数,都是方法,可以调用;
setTimeout(function(){
var flag = false;
if(flag) {
resolve('hello'); // 成功时调用 resolve()
}else{
reject('出错了'); // 失败时调用 reject()
}
}, 100);
});
//用then方法指定 resolved状态和 reject状态的回调函数
p.then(function(data){
//从resolve得到,成功时的处理结果 (then里的this不是vue实例,要用箭头函数)
},function(info){
//从reject得到,失败时的处理结果
});
2) Promise 发送多次Ajax请求 并保证顺序
//return一个新的,Promise实例对象,下一个then的调用者,
//就是上面 return出来的 Promise实例对象;data接受上一个异步任务,的处理结果;
queryData('http://localhost:3000/data') //queryData()相当于p
.then(function(data){
console.log(data)
return queryData('http://localhost:3000/data1');//返回值,是一个新的 Promise实例对象
})
.then(function(data){//得到新Promise()异步任务的结果;
console.log(data);
return queryData('http://localhost:3000/data2');
})
.then(function(data){
console.log(data)
});
3) then参数中,函数的返回值:
1、返回 Promise实例对象:
返回的该实例对象,会调用下一个then;在下一个then中,得到上一个,异步任务的处理结果;
2、返回普通值:
会直接传递给下一个then;会产生一个新的,默认的 Promise实例对象,调用下一个then;
4) console.dir(Promise)
1. Promise 常用API-实例方法 (位于Promise原型中的方法,通过实例.对象调用)
P.then() 得到成功时,执行的正确结果;
P.catch() 获取异常信息:和then的错误方法等效,接收 reject() 传递过来的信息;
P.finally() 成功与否都会执行(尚不是正式标准):作一些提示信息,或销毁一些资源;
function foo() {
return new Promise(function(resolve, reject) {
setTimeout(function() {
reject('error');
}, 100);
})
}
foo()
.then(function(data){
console.log(data)
}
.catch(function(data){
console.log(data) //error 接收reject传递过来的异常信息
})
.finally(function(){
console.log('finished')
});
2. Promise 常用API-对象方法 (直接通过Promise 函数名称调用;与prototype原型对象平级)
Promise.all().then() 并发处理多个异步任务,所有任务都执行完,才能得到结果;
接受一个数组,作为参数;数组中均为 promise实例对象,每一个实例对象,完成一个异步的操作;结果也是一个数组,和传参时一 一对应;
Promise.race().then() 并发处理多个异步任务,只要有一个任务完成,就能得到结果
同样接受一个数组,作为参数;把第一个改变状态的 promise的返回值,传给回调函数;只得到一个,最快返回的结果;
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){
//all中的参数 [p1,p2,p3],和返回结果一 一对应 ["HELLO TOM","HELLO JERRY","HELLO SPIKE"]
console.log(result)
})
Promise.race([p1,p2,p3]).then(function(result){
// 由于p1执行较快,Promise的then()将获得结果'P1';p2、p3仍继续执行,但执行结果将被丢弃;
console.log(result) // HELLO TOM
})
3. 接口调用-fetch API
- fetch 基于Promise实现;默认是get请求,返回一个 Promise实例对象;
- fetch 就是 ajax + Promise,使用的方式和 jquery 提供的 $.ajax() 差不多;
- fetch 不是 ajax 的进一步封装,而是原生 js,没有使用 XMLHttpRequest对象;
fetch('http://localhost:3000/fdata').then(function(data){
//text()属于fetch API的一部分,它返回一个Promise实例对象,
//return把它返回,通过下一个 then得到具体的数据
return data.text(); //二进制
}).then(function(data){
console.log(data); //这里得到的,才是最终数据
}).catch(function(info){
console.log(info);
})
1) fetch 请求参数:在url后面,添加一个{对象},用于配置请求信息
常用配置项:
method:请求方法,默认get (delete、post、put)
body:请求参数(post、put请求用)
headers:请求头,默认为{}
// GET参数传递 - restful形式的URL
fetch('http://localhost:3000/books/456', {
method: 'get'/'delete'
})
.then(function(data) {
return data.text();
}).then(function(data) {
console.log(data)
});
// POST请求传参 (PUT请求,要加 id/123)
fetch('http://localhost:3000/books', {
method: 'post',
body: JSON.stringify({ //把对象转换成json格式的字符串
uname: '张三',
pwd: '456'
}),
headers: { //json格式请求
'Content-Type': 'application/json'
}
})
.then(function(data) {
return data.text();
}).then(function(data) {
console.log(data)
});
2) fetch 响应结果的数据格式
- 用 fetch 来获取数据,返回的是一个 response对象,其中包括一堆原始字节;需要通过调用方法,将其转换为,相应格式的数据,比如 JSON,BLOB 或者 TEXT 等等;
fetch('http://localhost:3000/json').then(function(data){
return data.json(); //和JSON.parse(data)作用一样,将字符串,转换成json形式的对象;
}).then(function(data){
console.log(data) //Object age:13 uname:'lisi' gender:'male'
console.log(data.uname) //lisi
})
-----------------------------------------------------------------------------------------
fetch('http://localhost:3000/json').then(function(data){
return data.text(); //将返回的数据,转换成字符串;(对象内是字符串)
}).then(function(data){
console.log(data) //{"uname":"lisi","age":"13","gender":"male"},这是字符串,不是对象
console.log(typeof data) //string,字符串无法通过 .的形式,得到 uname
var obj = JSON.parse(data); //转换成对象
console.log(obj.uname,obj.age,obj.gender) //lisi 13 male
})
// json() 对象形式的,不用转换数据;
// text() 字符串形式的,要多一步 JSON.parse(data),转换成对象;
var obj = {
uname: 'lisi',
age: 12
}
for (var key in obj) {
console.log(obj[key])
}
//obj对象中,没有 key这个属性,所以obj.key=undefined;obj[key]=lisi 12
4. 接口调用-axios
- 基于 promise 用于浏览器和 node.js 的 http客户端;(http客户端:可以调用一个http形式的后台接口)
- 专门的,第三方的 js库,用来实现接口的调用;(需要引入 axios.js库文件)
- 支持浏览器和 node.js
- 能拦截请求和响应
- 自动转换 JSON数据
- 能转换请求和响应数据
1) axios基础用法
axios.get('http://localhost:3000/adata')
.then(function(ret){
console.log(ret) //返回的是一个对象,data是其中的 一个属性;
console.log(ret.data) //data属于axios API的一部分,用于获取后台数据;
})
2) axios 常用API
get: 查询数据
delete:删除数据
post: 添加数据
put: 修改数据
get和 delete请求传参
#1.1 get请求-通过传统'url',以'?'的形式传参;
axios.get('http://localhost:3000/axios?id=123')
.then(function(ret){
console.log(ret.data)
})
#1.2 get请求-通过'restful'形式的 url传参;
axios.get('http://localhost:3000/axios/123')
.then(function(ret){
console.log(ret.data)
})
#1.3 get请求-通过'params'形式传参;(后台接口和传统url用的接口一样)
axios.get('http://localhost:3000/axios', {
params: {
id: 789
}
}).then(function(ret){
console.log(ret.data)
})
#2 delete请求-传参形式和 get一样
axios.delete('http://localhost:3000/axios', {
params: {
id: 111
}
}).then(function(ret){
console.log(ret.data)
})
post和 put请求传参
#3.1 post请求-通过对象传参 (默认传递的是 Json格式的数据)
axios.post('http://localhost:3000/axios', {
uname: 'lisi',
pwd: 123 //提交Json格式的数据: {"uname":"lisi","pwd":123},需要后台提供支持;
}).then(function(ret){
console.log(ret.data)
})
#3.2 post请求-通过'URLSearchParams'传参
var params = new URLSearchParams();
params.append('uname', 'lisi');
params.append('pwd', '111'); //提交表单形式的数据:字符串'uname=lisi&pwd=111'
axios.post('http://localhost:3000/axios', params)
.then(function(ret){
console.log(ret.data)
})
//表单上传编码格式为 application/x-www-form-urlencoded (Request Headers中),参数格式为key=value&key=value
//URLSearchParams()构造函数,产生一个实例对象,把参数加到实例对象里,然后整体作为参数传递;
#4 put请求-传参形式和 post一样 (多传一个修改的id)
axios.put('http://localhost:3000/axios/123', {
uname: 'lisi',
pwd: 123
}).then(function(ret){
console.log(ret.data)
})
3) axios 的响应结果
请求发出去后,后台响应对应的结果,axios把这个结果,封装成了一个对象:
data 响应回来的数据,json形式的
headers 响应头信息
status 响应状态码:200
statusTest 响应状态信息:"ok"
4) axios 全局配置
// 配置默认地址: 会自动拼接,简写的请求路径;
axios.defaults.baseURL = 'https://api.example.com/';
// 配置超时时间: 3000ms内不响应,请求出错;
axios.defaults.timeout = 3000;
// 配置请求头: 登录时会用到,携带token信息,登录成功;
axios.defaults.headers['Authorization'] = AUTH_TOKEN;
// 配置公共 post的 Content-Type
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
// axios.get('http://localhost:3000/json')
axios.defaults.baseURL = 'http://localhost:3000/';
axios.defaults.headers['mytoken'] = 'hello';
//设置完请求头后,发出请求时,会在请求头中,添加这个属性,值是后面指定的值;
axios.get('json').then(function(ret){
console.log(ret.data.uname)
})
//对于跨域来说,请求头是需要,后台来配置的;res.header('','mytoken')允许传递'mytoken';
5) axios 拦截器
1.请求拦截器:在请求发送前,设置一些信息;比如:在每个请求体里,加上token;
//第一个函数:通过形参 config,来做信息的设置;config 一定要return出去,否则配置不生效;
//第二个函数:用于处理响应的,错误信息
axios.interceptors.request.use(function(config) {
//console.log(config.url) //得到请求的 url地址,可以根据 url来判断,添加请求头;
config.headers.mytoken = 'nihao'; //配置请求头;与 axios.defaults.headers['mytoken']类似
return config; //一定要return
}, function(err){
console.log(err)
})
2.响应拦截器:在获取数据之前,进行加工处理;
//例如:服务器返回登录状态失效,需要重新登录时,跳转到登录页
axios.interceptors.response.use(function(res) {
//res:axios返回的包装对象,通过对象的.data,才能拿到数据
//对返回的数据,进行处理;只要具体的数据,不要包装的对象;
var data = res.data;
return data;
}, function(err){
console.log(err)
})
axios.get('http://localhost:3000/adata').then(function(data){
console.log(data) //hello axios;请求头Request Headers中 mytoken:nihao
})
5. 接口调用-async/await
- async/await ES7语法;async关键字,放到函数前面,async函数隐式返回一个 Promise实例对象;
- await关键字,只能用于async函数中,await后面要跟一个,Promise实例对象,用于得到异步处理的结果;
//传统的 axios方式,调用接口
axios.get('http:localhost:3000/adata').then(function(ret){
console.log(ret.data) //hello axios
})
async function queryData() {
var ret = await axios.get('http:localhost:3000/adata');
console.log(ret.data) //hello axios
}
queryData()
//async函数内部,把异步结果返回出去;调用queryData(),通过.then的方式,获取上面的返回值;
async function queryData() {
var ret = await axios.get('http:localhost:3000/adata');
return ret.data; //把异步结果返回出去
}
queryData().then(function(data){
console.log(data) //hello axios
})
// await后面要跟一个,Promise实例对象,在Promise中,处理异步任务;
async function queryData() {
var ret = await new Promise(function(resolve, reject){
setTimeout(function(){
resolve('nihao')
},1000);
})
return ret;
}
// 任何一个async函数,都会隐式返回一个promise;可以使用.then,进行链式编程;
queryData().then(function(data){
console.log(data) //nihao
})
- async/await 处理多个异步请求
axios.defaults.baseURL = 'http://localhost:3000';
async function queryData() {
//当前的 await 返回结果后,才会执行后面的代码;info请求结果,作为第二个请求的参数;
var info = await axios.get('async1');
var ret = await axios.get('async2?info=' + info.data); //info=info.data
return ret.data;
}
// 后台 if(req.query.info=='hello'){res.send('world')}else{res.send('error')}
queryData().then(function(data){
console.log(data)
})
6. 图书列表案例
router.js //提供了六个后台接口
service.js //具体的后台接口业务功能
data.json //模拟数据库
index.js //入口文件
启动后台服务: 终端 nodemon -->默认执行的是index.js
1) 图书列表加载
导入axios 用来发送ajax
<div id="app">
<table>
<thead> ... ... </thead>
<tbody>
<!-- 把 books 中的数据,渲染到页面上 -->
<tr :key='item.id' v-for='item in books'>
<td>{{item.id}}</td>
<td>{{item.name}}</td>
<td>{{item.date }}</td>
<td>
<a href="">修改</a>
<span>|</span>
<a href="">删除</a>
</td>
</tr>
</tbody>
</table>
</div>
<script type="text/javascript" src="js/vue.js"></script>
1.导入axios.js
<script type="text/javascript" src="js/axios.js"></script>
<script type="text/javascript">
# 2.配置基准路径
axios.defaults.baseURL = 'http://localhost:3000/';
# 3.配置响应拦截器 data = res.data
axios.interceptors.response.use(function(res) {
return res.data;
}, function(error) {
console.log(error)
});
var vm = new Vue({
el: '#app',
data: {
flag: false,
submitFlag: false,
id: '',
name: '',
books: []
},
methods: {
# 4.获取图书列表数据
queryData: async function() {
// var ret = await axios.get('books');
// this.books = ret.data;
# 把获取的数据,放在books 里面
this.books = await axios.get('books');
}
},
mounted: function() {
# 5.mounted: DOM加载完毕,立即调用函数
// var that = this;
// axios.get('books').then(function(data){
// that.books = data.data;
// })
//.then 里的 this,不是vue实例,要用箭头函数
// axios.get('books').then((data)=>{
// this.books = data.data;
// })
this.queryData();
}
});
</script>
2) 添加图书
methods: {
handle: async function(){
if(this.flag) {
// 编辑图书:根据当前的ID,更新数组中对应的数据
this.books.some((item) => {
if(item.id == this.id) {
item.name = this.name;
return true; //完成更新操作后,终止循环
}
});
this.flag = false;
}else{
# 获取用户输入的数据,发送添加图书 ajax请求
var ret = await axios.post('books', {
name: this.name
})
# 根据返回的状态码,判断是否成功;成功,重新加载列表数据
if(ret.status == 200) {
this.queryData(); //渲染最新的数据
}
}
// 清空表单
this.id = '';
this.name = '';
}
}
3) 验证图书名称是否存在
watch: {
name: async function(val) {
// 验证图书名称是否已经存在
// var flag = this.books.some(function(item){
// return item.name == val;
// });
var ret = await axios.get('/books/book/' + this.name); //this.name也可以是val
if(ret.status == 1) {
// 图书名称存在,禁用 "提交" 按钮
this.submitFlag = true;
}else{
// 图书名称不存在,可以提交
this.submitFlag = false;
}
}
}
4) 编辑图书
根据状态位,判断是添加,还是编辑;
methods: {
handle: async function(){
if(this.flag) {
# 把用户输入的信息,提交到后台
var ret = await axios.put('books/' + this.id, {
name: this.name
});
if(ret.status == 200){
# 添加成功,重新加载数据
this.queryData();
}
this.flag = false; # 修改完,flag状态位,重新设为false
}else{
// 添加图书
var ret = await axios.post('books', {
name: this.name
})
if(ret.status == 200) {
// 重新加载列表数据
this.queryData();
}
}
// 清空表单
this.id = '';
this.name = '';
},
toEdit: async function(id){
# input框已经禁用;flag状态位,默认为false,添加操作;为true时,编辑操作;
this.flag = true;
# 根据id,发送ajax;加载出,要编辑图书的,最新信息
var ret = await axios.get('books/' + id);
this.id = ret.id;
this.name = ret.name;
}
}
5) 删除图书
deleteBook: async function(id){
// 把需要删除的书籍id,传给后台
var ret = await axios.delete('books/' + id);
if(ret.status == 200) {
// 重新加载列表数据
this.queryData();
}
}