1. 前后端交互模式
1.1 接口调用方式
- 原生Ajax
- 基于jQuery的ajax
- fetch
- axios
1.2 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.itcast.cn
② http://www.itcast.cn/java/web
③ http://www.itcast.cn/java/web?flag=1
④ http://www.itcast.cn/java/web?flag=1#function
2. Restful形式的URL
- HTTP请求方式
① GET 查询
② POST 添加
③ PUT 修改
④ DELETE 删除 - 符合规则的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用法
2.1 异步调用
- 异步效果分析
① 定时任务
② Ajax
③ 事件函数 - 多次异步调用的依赖分析
① 多次异步调用的结果顺序不确定
// 三者的顺序不确定
$.ajax({
url:'http://localhost:3000/data',
success:function(data){
console.log(data)
}
});
$.ajax({
url:'http://localhost:3000/data1',
success:function(data){
console.log(data)
}
});
$.ajax({
url:'http://localhost:3000/data2',
success:function(data){
console.log(data)
}
});
② 异步调用结果如果存在依赖需要嵌套(出现回调地狱)
$.ajax({
url:'http://localhost:3000/data',
success:function(data){
console.log(data)
$.ajax({
url:'http://localhost:3000/data1',
success:function(data){
console.log(data)
$.ajax({
url:'http://localhost:3000/data2',
success:function(data){
console.log(data)
}
});
}
});
}
});
2.2 Promise概述
Promise是异步编程的一种解决方案,从语法上讲, Promise是一个对象,从它可以获取异步操作的消息。
使用 Promise主要有以下好处:
- 可以避免多层异步调用嵌套问题(回调地狱)
- Promise对象提供了简洁的APl,使得控制导步操作更加容易
官方文档:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
2.3 Promise基本用法
- 实例化Promise对象,构造函数中传递函数,该函数中用于处理异步任务
- resolve和reject两个参数用于处理成功和失败两种情况,并通过
p.then
获取处理结果
var p = new Promise(function(resolve,reject){
// 成功时调用 resolve()
resolve(data)
// 失败时调用 reject()
reject(err)
});
p.then(function(data){
// 从resolve得到正常结果,data为resolve中的参数
},function(err){
// 从reject得到错误信息,err为reject中的参数
});
补充:Promise的链式调用
- 成功请求普通写法
new Promise((resolve,reject)=>{
// 模拟网络请求
setTimeout(()=>{
resolve('aaa')
},1000)
}).then(res=>{
console.log(res); //aaa
return new Promise(resolve=>{
resolve('bbb')
})
}).then(res=>{
console.log(res); //bbb
return new Promise(resolve=>{
resolve('ccc')
})
}).then(res=>{
console.log(res) //ccc
})
- 成功请求简单写法(对上述过程化简)
new Promise((resolve,reject)=>{
// 模拟网络请求
setTimeout(()=>{
resolve('aaa')
},1000)
}).then(res=>{
console.log(res); //aaa
return Promise.resolve('bbb')
}).then(res=>{
console.log(res); //bbb
return Promise.resolve('ccc')
}).then(res=>{
console.log(res) //ccc
})
- 失败请求简单写法
new Promise((resolve,reject)=>{
// 模拟网络请求
setTimeout(()=>{
resolve('aaa')
},1000)
}).then(res=>{
console.log(res) // aaa
return Promise.reject('error message')
// 上面这句话也可以这样调用
// throw 'error message'
}).then(res=>{
console.log(res) // 这句话不执行
}).catch(err=>{
console.log(err) // error message
})
2.4 基于Promise处理Ajax请求
1. 处理原生Ajax
function queryData(url){
var p = 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)
});
return p
}
queryData('http://localhost:3000/data')
.then(function(data){
console.log(data)
},function(info){
console.log(info)
});
2. 发送多次Ajax请求
queryData('http://localhost:3000/data')
.then(function(data){
console.log(data)
return queryData('http://localhost:3000/data1')
})
.then(function(data){
console.log(data)
return queryData('http://localhost:3000/data2')
})
.then(function(data){
console.log(data)
});
2.5 then参数中的函数返回值
- 返回Promise实例对象
- 返回该实例对象会调用下一个then
- 返回普通值
- 返回的普通值会直接传递给下一个then,通过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)
},1000)
})
})
.then(function(data){
// 1
console.log(data) // 123
// 2 产生一个默认的promise实例对象
return 'hello'
})
.then(function(data){
console.log(data) // hello
});
2.6 Promise常用的API
1. 实例方法
- p.then() 得到异步任务的正确结果
- p.catch() 获取异常信息
- p.finally() 成功与否都会执行(尚且不是正式标准)
function foo(){
return new Promise(function(resolve,reject){
setTimeout(function(){
resolve(123)
// reject('error')
},100)
})
}
foo()
.then(function(data){
console.log(data) // 正常时执行 123
})
.catch(function(data){
console.log(data) // 异常时执行 error
})
.finally(function(){
console.log('finished') // 无论正常、异常都执行
})
- 对象方法
- Promise.all() 并发处理多个异步任务,所有任务都执行完成才能得到结果
- Promise.race() 并发处理多个异步任务,只要有一个任务完成就能得到结果
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: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)
}) // 返回第一个返回的结果
3. 接口调用-fetch用法
3.1 fetch概述
1. 基本特征
- 更加简单的数据获取方式,功能更强大、更灵活,可以看做是xhr的升级版
- 基于Promise实现
2. 语法结构
fetch(url)
.then(fn2)
.then(fn3)
...
.catch(fn)
官方文档:https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API
3.2 fetch的基本用法
fetch('/abc').then(data => {
return data.text() // 返回一个Promise实例对象
}).then(ret=>{
// 注意这里得到的才是最终的数据
console.log(ret)
})
text()方法属于fetchAPI的一部分,它返回一个Promise实例对象,用于获取后台返回的数据
3.3 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() // 返回一个Promise实例对象
}).then(ret=>{
// 注意这里得到的才是最终的数据
console.log(ret)
})
2. GET请求方式的参数传递
方式1:
// client发送,从server端返回
fetch('/abc?id=123',{
method:'GET'
}).then(data => {
return data.text() // 返回一个Promise实例对象
}).then(ret=>{
console.log(ret)
})
// server接收,并发送给client
app.get('/abc',(req,res)=>{
res.send('传统的URL传递参数!' + req.query.id)
})
方式2:
// client发送,从server端返回
fetch('/abc/123',{
method:'GET'
}).then(data => {
return data.text() // 返回一个Promise实例对象
}).then(ret=>{
console.log(ret)
})
// server接收,并发送给client
app.get('/abc/:id',(req,res)=>{
res.send('Restful形式的URL传递参数!' + req.params.id)
})
3. DELETE请求方式的参数传递
// client发送,从server端返回
fetch('/abc/123',{
method:'DELETE'
}).then(data => {
return data.text() // 返回一个Promise实例对象
}).then(ret=>{
console.log(ret)
})
// server接收,并发送给client
app.delete('/abc/:id',(req,res)=>{
res.send('DELETE请求传递参数!' + req.params.id)
})
4. POST请求方式的参数传递
方式1(普通传参):
// client发送,从server端返回
fetch('/abc',{
method:'POST',
body:'uname=lisi&pwd=123',
headers:{
'Content-Type':'application/x-www-form-urlencoded'
}
}).then(data => {
return data.text() // 返回一个Promise实例对象
}).then(ret=>{
console.log(ret)
})
const bodyParser = require('body-parser')
// server接收,并发送给client
app.post('/abc',(req,res)=>{
res.send('POST请求传递参数!' + req.body.uname + '----' + req.body.pwd)
})
方式2(JSON传参):
// client发送,从server端返回
fetch('/abc',{
method:'POST',
body:JSON.stringify({
uname: 'lisi',
age:12
}),
headers:{
'Content-Type':'application/json'
}
}).then(data => {
return data.text() // 返回一个Promise实例对象
}).then(ret=>{
console.log(ret)
})
const bodyParser = require('body-parser')
const app = express()
app.use(bodyParser.json())
// server接收,并发送给client
app.post('/abc',(req,res)=>{
res.send('POST请求传递参数!' + req.body.uname + '----' + req.body.pwd)
})
5. PUT请求方式的参数传递
// client发送,从server端返回
fetch('/abc/123',{
method:'PUT',
body:JSON.stringify({
uname: 'lisi',
age:12
}),
headers:{
'Content-Type':'application/json'
}
}).then(data => {
return data.text() // 返回一个Promise实例对象
}).then(ret=>{
console.log(ret)
})
const bodyParser = require('body-parser')
const app = express()
app.use(bodyParser.json())
// server接收,并发送给client
app.put('/abc/:id',(req,res)=>{
res.send('PUT请求传递参数!' + req.params.id + '----' + req.body.uname + '----' + req.body.pwd)
})
3.4 fetch响应结果
响应数据格式
- text():将返回体处理成字符串类型
- json():返回结果和JSON.parse(responseText)一样
// client发送,从server端返回
fetch('/abc').then(data => {
// return data.text()
return data.json()
}).then(data=>{
console.log(data) // JSON对象
console.log(data.uname) // JSON对象.属性
})
// server接收,并发送给client
app.get('/abc',(req,res)=>{
res.json({
uname: 'lisi',
age: 13,
gender: 'male'
})
})
附:字符串转JSON方法——var obj = JSON.parse(str)
4. 接口调用-axios用法
4.1 axios的基本特征
axios(官网:https://github.com/axios/axios)是一个基于Promise用于浏览器和node.js的HTTP客户端。
它具有以下特征:
- 支持浏览器和node.js
- 支持promise
- 能拦截请求和响应
- 自动转换JSON数据
4.2 axios的基本用法
项目安装Axios:
cnpm install axios --save
基本使用:
axios({
url:'/adata',
method:'GET' // 默认GET方法
}).then(ret=>{
// data 属性名称是固定的,用于获取后台响应的实际数据
console.log(ret.data)
})
4.3 axios的常用API
- get:查询数据
- post:添加数据
- put:修改数据
- delete:删除数据
注:axios发送并发请求
- 有时候,我们可能需要同时发送两个请求
- 使用axios.all,可以放入多个请求的数组
- axios.all([])返回的结果是一个数组 ,使用axios.spread可将数组[res1,res2]展开为res1,res2
// axios发送并发请求,两个请求同时相应之后才有结果
axios.all([axios({
url:'/home/data1'
}), axios({
url:'/home/data2'
})]).then(results=>{
console.log(results) // 返回一个数组[res1,res2]
})
spread:
axios.all([axios({
url:'/home/data1'
}), axios({
url:'/home/data2'
})]).then(axios.spread((res1,res2) => {
console.log(res1) // res1
console.log(res2) // res2
}))
4.4 axios的参数传递
1. GET传递参数
- 通过URL传递参数
方法1:
axios.get('/adata?id=123')
.then(ret=>{
console.log(ret.data) // axios get 传递参数123
})
// server接收,并发送给client
app.get('/adata',(req,res)=>{
res.send('axios get 传递参数' + req.query.id)
})
方法2:
axios.get('/adata/123')
.then(ret=>{
console.log(ret.data) // axios get (Restful) 传递参数123
})
// server接收,并发送给client
app.get('/adata/:id',(req,res)=>{
res.send('axios get (Restful) 传递参数' + req.params.id)
})
- 通过params选项传递参数
axios.get('/adata',{
params:{
id:123
}
})
.then(ret=>{
console.log(ret.data) // axios get 传递参数123
})
// server接收,并发送给client
app.get('/adata',(req,res)=>{
res.send('axios get 传递参数' + req.query.id)
})
2. DELETE传递参数
- 参数传递方式与GET类似
方法1:
axios.delete('/adata?id=123')
.then(ret=>{
console.log(ret.data)
})
方法2:
axios.delete('/adata/123')
.then(ret=>{
console.log(ret.data)
})
方法3:
axios.delete('/adata',{
params:{
id:123
}
})
.then(ret=>{
console.log(ret.data)
})
3. POST传递参数
- 通过选项传递参数(默认传递的是json格式的数据)
axios.post('/adata',{
uname:'tom',
pwd:123
}).then(ret=>{
console.log(ret.data)
})
// server接收,并发送给client
app.post('/adata',(req,res)=>{
res.send('axios post 传递参数' + req.body.uname + req.body.pwd)
})
- 通过URLSearchParams传递参数(application/x-www-form-urlencoded)
const params = new URLSearchParams();
params.append('uname','zhangsan');
params.append('pwd','111');
axios.post('/adata',params).then(ret=>{
console.log(ret.data)
})
// server接收,并发送给client
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)
})
// server接收,并发送给client
app.put('/adata/:id',(req,res)=>{
res.send('axios post 传递参数' + req.params.id + req.body.uname + req.body.pwd)
})
4.5 axios 的响应结果
响应结果的主要属性
- data:实际响应回来的数据
- headers:响应头信息
- status:响应状态码
- statusText:响应状态信息
data为例:
axios.get('/adata')
.then(ret=>{
console.log(ret.data.uname) // lisi
})
// server接收,并发送给client
app.post('/adata',(req,res)=>{
res.json({
uname:'lisi',
age:12
});
})
4.6 axios的全局配置
axios.defaults.timeout = 3000
// 超时时间axios.defaults.baseURL = 'http://localhost:3000/app'
// 默认地址axios.defaults.headers[' mytoken'] = 'aqwerwqwerqwere2ewe23eresdf23'
// 设置请求头
配置请求的基准URL地址(默认地址):
axios.defaults.baseURL = 'http://localhost:3000/'
axios.get('adata') // 拼接成:http://localhost:3000/adata
.then(ret=>{
console.log(ret.data.uname) // lisi
})
配置请求头信息:
axios.defaults.headers[' mytoken'] = 'aqwerwqwerqwere2ewe23eresdf23'
4.7 axios创建实例
- 当我们从axios模块中导入对象时,使用的实例是默认的axios实例
- 当给该实例设置一些默认配置时,这些配置就被固定下来了
- 但是后续开发中,某些配置可能会不太一样。
- 比如某些请求需要使用特定的baseURL或者timeout或者content-type等
- 这个时候,我们就可以创建新的实例,并且传入属于该实例的配置信息
const instance1 = axios.create({
baseURL: 'http://192.168.0.1:8000',
timeout:5000
})
const instance2 = axios.create({
baseURL: 'http://192.168.0.2:8000',
timeout:8000
})
instance1({
url:'/home/data1'
}).then(res=>{
console.log(res)
})
4.8 axios拦截器
1. 请求拦截器
在请求发出之前设置一些信息,一般包括如下情况:
- 比如config中的一些信息不符合服务器的要求
- 比如每次发送网络请求时,都希望在界面中显示一个请求图标
- 某些网络请求(比如登陆(token)),必须携带一些特殊信息
axios.interceptors.request.use(function(config){
// 在请求发出之前进行一些信息设置
config.headers.mytoken = 'nihao';
return config
},function(err){
// 处理响应的错误信息
console.log(err)
})
// axios.get().then...
2. 响应拦截器
在获取数据之前对数据做一些加工处理
axios.interceptors.response.use(function(res){
// 在这里对返回的数据进行处理
var data = res.data
return data
},function(err){
// 处理响应的错误信息
console.log(err)
})
// axios.get().then...
4.9 axios的模块封装
对于不同组件调用axios时,需要在每个组件中引入axios的配置信息,一旦发生改变需要在每个组件中修改,非常麻烦。
因此最好的方式是引入一个全局Axios配置文件,并将公共部分封装起来,其他组件使用使直接引入即可。需要修改时(比如baseURL等信息),只需要修改这个全局配置文件即可,无需在每个组件中进行修改。
在src下新建一个名为network
的文件夹,并在文件夹内新建一个名为request.js
配置文件
request.js:
import axios from 'axios'
export function request(config) {
// 1.创建axios实例
const instance = axios.create({
baseURL: 'http://192.168.0.1',
timeout: 5000
})
// 2. axios的拦截器
// 2.1 请求拦截器
instance.interceptors.request.use(config => {
// console.log(config); // 拦截请求信息,这块处理请求前的操作
return config // 拦截之后必须得return出去,不然后台接收不到请求
}, err => {
console.log(err);
})
// 2.2 响应拦截器
instance.interceptors.response.use(res => {
// 这块处理响应前的操作
return res.data
},err => {
console.log(err);
})
// 3.返回promise对象,在组件中执行then、catch等
return instance(config)
}
组件中调用:
import { request } from '../network/request.js';
export default {
name: '',
data() {
return {};
},
created() {
// 封装request模块
request({
url: '/home/data'
}).then(res => {
// 成功时执行
console.log(res);
}).catch(err => {
// 异常时执行
console.log(err);
});
}
};
5. 接口调用-async/await用法
5.1 async/await的基本用法
- async/await是ES7引入的新语法,可以更加方便的进行异步操作
- async关键字用于函数上(async函数的返回值是Promise实例对象)
- await关键字用于async函数当中(await可以得到异步的结果,也可以返回Promise对象)
简单示例:
function ysz(){
return new Promise((resolve, reject)=>{
let sino = parseInt(Math.random() * 6 +1)
setTimeout(()=>{
resolve(sino)
},3000)
})
}
async function test(){
let n =await ysz()
console.log(n)
}
test()
await返回Promise对象:
async function queryData(){
const ret = await axios.get('/data')
return ret.data
}
queryData().then(ret=>{
console.log(ret)
})
await返回异步结果:
async function queryData(){
var ret = await new Promise(function(resolve,reject){
setTimeout(function(){
resolve('nihao')
},1000)
})
return ret
}
queryData().then(data=>{
console.log(data) // nihao
})
5.2 async/await处理多个异步请求
多个异步请求的场景
axios.defaults.baseURL = 'http://localhost:3000'
async function queryData(){
var info = await axios.get('data1')
var ret = await axios.get('data2?info=' + info.data)
return ret.data
}
queryData().then(data=>{
console.log(data) // world
})
// server接收,并发送给client
app.get('/data1',(req,res)=>{
res.send('hello');
})
app.get('/data2',(req,res)=>{
if(req.query.info == 'hello'){
res.send('world')
}else{
res.send('error')
}
})