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对象,构造函数中传递函数,该函数中用于处理异步任务
  • resolvereject两个参数用于处理成功失败两种情况,并通过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. 基于接口的案例

基于接口的图书管理功能

posted @ 2020-06-19 10:24  浮华夕颜  Views(985)  Comments(0Edit  收藏  举报