• 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. 基于接口的案例

    基于接口的图书管理功能

  • 相关阅读:
    NHibernate 做个小项目来试一下吧 四 (我们继续)
    NHibernate 做个小项目来试一下吧 三
    NHibernate 做个小项目来试一下吧(数据分页) 七
    用SWFUpload插件进行多文件上传(上传页获得自定义后的文件名)
    SQL:找出我(uid=2)所有的好友信息,和这些好友发布的最新的一篇文章
    介绍生成PHP网站页面静态化的方法
    smarty if 操作符
    php 做注册邮件发送成功
    200多个js技巧代码
    生成列表页分页的HTML静态页
  • 原文地址:https://www.cnblogs.com/zcy9838/p/13156522.html
Copyright © 2020-2023  润新知