Vue2 基础四前后端交互

代码下载

前后端交互模式

接口调用方式:原生ajax、基于jQuery的ajax、fetch、axios。

URL 地址格式

传统形式的 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),用于定位页面的某个位置

Restful 形式的 URL HTTP请求方式:GET 查询、POST 添加、PUT 修改、DELETE 删除。

Promise

官网地址

JavaScript的执行环境是单线程,常见的异步调用:定时任务、ajax、事件函数。多次异步调用的结果顺序不确定,异步调用结果如果存在依赖需要嵌套。

                    // 定时任务
                    let v = 1;
                    setTimeout(() => {
                        v = 2;
                    }, 1000);
                    console.log('v: ', v);
                    
                    // 多次异步调用
                    $.get('http:localhost/data1', function(data) {
                        console.log('data: ', data);
                    });
                    $.get('http:localhost/data2', function(data) {
                        console.log('data: ', data);
                    });
                    $.get('http:localhost/data3', function(data) {
                        console.log('data: ', data);
                    });
                    
                    // 嵌套调用
                    $.get('http:localhost/data1', function(data) {
                        console.log('data: ', data);
                        $.get('http:localhost/data2', function(data) {
                            console.log('data: ', data);
                            $.get('http:localhost/data3', function(data) {
                                console.log('data: ', data);
                            });
                        });
                    });

Promise 是异步编程的一种解决方案,从语法上讲,Promise是一个对象,从它可以获取异步操作的消息。使用 Promise 主要有以下好处:

  • 可以避免多层异步调用嵌套问题(回调地狱)
  • Promise 对象提供了简洁的API,使得控制异步操作更加容易

Promise 基本用法

实例化 Promise 对象,构造函数中传递函数,该函数用于处理异步任务,resolve 和 reject 两个参数用于处理成功和失败两种情况,并通过 p.then 获取处理结果:

 var p = new Promise(function(resolve, reject){
    // 成功时调用 resolve()
    // 失败时调用 reject()
 });
 p.then(funciton(ret){
    // 从resolve得到正常结果
 }, function(ret){
    // 从reject得到错误信息
 });
 
 
let p = new Promise(function(resolve, reject) {
    setTimeout(() => {
        let f = true;
        if (f) {
            resolve('正常');
        } else {
            reject('出错啦');
        }
    }, 1000);
});
p.then(function(data) {
    console.log('data: ', data);
}, function(info) {
    console.log('info: ', info);
});

基于Promise处理Ajax请求

处理原生Ajax:

                    let p = new Promise(function(resolve, reject) {
                        let xhr = new XMLHttpRequest();
                        xhr.open('GET', 'http:localhost/data1');
                        xhr.send();
                        xhr.onreadystatechange = function() {
                            if (xhr.readyState === 4) {
                                if (xhr.status === 200) {
                                    resolve(xhr.responseText);
                                } else {
                                    reject('出错啦');
                                }
                            }
                        };
                    });
                    p.then(function(data) {
                        console.log('data: ', data);
                    }, function(info) {
                        console.log('info: ', info);
                    })

发送多次ajax请求:

                    function queryData(url) {
                        let p = new Promise(function(resolve, reject) {
                            let xhr = new XMLHttpRequest();
                            xhr.open('GET', url);
                            xhr.send();
                            xhr.onreadystatechange = function() {
                                if (xhr.readyState === 4) {
                                    if (xhr.status === 200) {
                                        resolve(xhr.responseText);
                                    } else {
                                        reject('出错啦');
                                    }
                                }
                            };
                        });
                        return p;
                    }
                    queryData('http:localhost/data1').then(function(data) {
                        console.log('data: ', data);
                        return queryData('http:localhost/data2');
                    }).then(function(data) {
                        console.log('data: ', data);
                        return queryData('http:localhost/data3');
                    }).then(function(data) {
                        console.log('data: ', data);
                    });

then参数中的函数返回值:

  • 返回 Promise 实例对象,返回的该实例对象会调用下一个 then

  • 返回普通值,返回的普通值会直接传递给下一个 then,通过 then 参数中的第一个函数的参数接收该值

                      function createPromise(flag) {
                          let q = new Promise(function(resolve, reject) {
                              setTimeout(() => {
                                  if (flag) {
                                      resolve('成功');
                                  } else {
                                      reject('失败');
                                  }
                              }, 1000);
                          });
                          return q;
                      }
                      createPromise(false).then(function(data) {
                          console.log('data: ', data);
                          return data;
                      }, function(info) {
                          console.log('info: ', info);
                          return info;
                      }).then(function(data) {
                          console.log('--data: ', data, '--');
                      }, function(info) {
                          console.log('--info: ', info, '--');
                      })
                      // 打印结果
                      // info:  失败
                      // --data:  失败 --
    

Promise常用的API

  • p.then() 得到异步任务的正确结果,也可以得到异步任务的正确结果和异常信息。

  • p.catch() 获取异常信息。

  • p.finally() 成功与否都会执行(尚且不是正式标准)。

  • Promise.all() 接受一个数组作参数,数组中的对象均为promise实例(如果不是一个 promise,该项会被用 Promise.resolve 转换为一个promise)。并发处理多个异步任务,所有任务都执行完成才能得到结果(全部任务成功才算成功,只要有一个任务异常就只能得到此任务的异常信息)。

  • Promise.race() 方法同样接受一个数组作参数。并发处理多个异步任务,只要有一个任务完成就能得到结果。

                      function queryData(flag, index) {
                          let p = new Promise(function(resolve, reject) {
                              setTimeout(() => {
                                  if (flag) {
                                      resolve('成功-' + index);
                                  } else {
                                      reject('失败-' + index);
                                  }
                              }, 1000);
                          });
                          return p;
                      }
                      queryData(true, 1).then(function(data){
                          console.log('then success data: ', data);
                      }).catch(function(info) {
                          console.log('catch info: ', info);
                      }).finally(function() {
                          console.log('finally');
                      });
                      // 打印结果:
                      // then success data:  成功-1
                      // finally
    
                      queryData(false, 2).then(function(data){
                          console.log('then success data: ', data);
                      }).catch(function(info) {
                          console.log('catch info: ', info);
                      }).finally(function() {
                          console.log('finally');
                      });
                      // 打印结果:
                      // catch info:  失败-2
                      // finally
    
                      Promise.all([queryData(true, 3), queryData(false, 4), queryData(false, 5)]).then(function(result) {
                          console.log('result: ', result);
                      }, function(info) {
                          console.log('error: ', info);
                      });
                      // 打印结果:
                      // error:  失败-4
    
                      Promise.race([queryData(true, 6), queryData(false, 7), queryData(true, 8)]).then(function(result) {
                          console.log('result: ', result);
                      }, function(info) {
                          console.log('error: ', info);
                      });
                      // 打印结果:
                      // result:  成功-6
    

fetch

官网地址

  • Fetch API是新的ajax解决方案,更加简单地获取数据,功能更强大、更灵活。

  • 语法结构 fetch(url, options).then(fn1).then(fn1).catch(fn2),Fetch会返回Promise,所以我们可以使用 then 拿到请求成功的结果。

  • fetch不是ajax的进一步封装,而是原生js,基于Promise实现,没有使用XMLHttpRequest对象。

                      fetch('http://localhost/data1').then(function(response) {
                          // response 是响应体
                          console.log('response: ', response);
                          // text()方法属于fetchAPI的一部分,它返回一个Promise实例对象,用于获取后台返回的数据
                          let r = response.text();
                          console.log('text: ', r);
                          return r;
                      }).then(function(data) {
                          // 这里得到的才是最终数据
                          console.log('data: ', data);
                      });
                      // 打印结果:
                      // response:  Response {type: 'cors', url: 'http://localhost/data1', redirected: false, status: 200, ok: true, ...}
                      // text:  Promise {[[PromiseState]]: 'pending', [[PromiseResult]]: undefined}
                      // data:  data1
    

fetch 请求参数,常用配置选项:

  • method(String): HTTP请求方法,默认为GET (GET、POST、PUT、DELETE)
  • body(String): HTTP的请求参数
  • headers(Object): HTTP的请求头,默认为{}

fetch 响应数据格式:

  • text():将返回体处理成字符串类型

  • json():返回结果和 JSON.parse(responseText)一样

                      // get
                      fetch('http://localhost/fdata?id=1').then(function(res) {
                          return res.text();
                      }).then(function(data) {
                          console.log('data: ', data);
                      });
                      
                      // get 动态参数
                      fetch('http://localhost/fdata/2', {
                          method: 'get'
                      }).then(function(res) {
                          return res.text();
                      }).then(function(data) {
                          console.log('data: ', data);
                      });
                      
                      // post url-encoded 参数
                      fetch('http://localhost/fdata', {
                          method: 'post',
                          body: 'uname=张三&password=123456',
                          headers: {
                              'Content-Type': 'application/x-www-form-urlencoded'
                          }
                      }).then(function(res) {
                          return res.text();
                      }).then(function(data) {
                          console.log('data: ', data);
                      });
                      
                      // post josn 参数
                      fetch('http://localhost/fdata', {
                          method: 'post',
                          body: JSON.stringify({
                              uname: '李四',
                              password: '123456'
                          }),
                          headers: {
                              'Content-Type': 'application/json'
                          }
                      }).then(function(res) {
                          return res.json();
                      }).then(function(data) {
                          console.log('data: ', data);
                      });
                      
                      // delete
                      fetch('http://localhost/fdata/3', {
                          method: 'delete'
                      }).then(function(res) {
                          return res.text();
                      }).then(function(data) {
                          console.log('data: ', data);
                      });
                      
                      // put
                      fetch('http://localhost/fdata/4', {
                          method: 'put',
                          body: JSON.stringify({
                              uname: '王五',
                              password: '123456'
                          }),
                          headers: {
                              'Content-Type': 'application/json'
                          }
                      }).then(function(res) {
                          return res.json();
                      }).then(function(data) {
                          console.log('data: ', data);
                      });
    

axios

axios(官网)是一个基于Promise 用于浏览器和 node.js 的 HTTP 客户端。它具有以下特征:

  • 支持浏览器和 node.js
  • 支持 promise
  • 能拦截请求和响应
  • 自动转换 JSON 数据

基本用法:

axios.get('/adata')
       .then(ret=>{
          // data属性名称是固定的,用于获取后台响应的数据
          console.log(ret.data)
       })

axios 的响应结果的主要属性:

  • data : 实际响应回来的数据
  • headers :响应头信息
  • status :响应状态码
  • statusText :响应状态信息

axios 的参数传递:

                    // get
                    axios.get('http://localhost/data?id=1')
                    .then(function(res) {
                        console.log(res.data);
                    });

                    // get 动态参数
                    axios.get('http://localhost/data/2')
                    .then(function(res) {
                        console.log(res.data);
                    });

                    // get params 形式参数
                    axios.get('http://localhost/data', {
                        params: {
                            id: 3
                        }
                    }).then(function(res) {
                        console.log(res.data);
                    });

                    // post 通过 URLSearchParams 传递url-encoded参数
                    let params = new URLSearchParams();
                    params.append('uname', '张三');
                    params.append('password', '123');
                    console.log(params);
                    axios.post('http://localhost/data', params).then(function(res) {
                        console.log(res.data);
                    });

                    // post json参数
                    axios.post('http://localhost/data', {
                        uname: '李四',
                        password: '123'
                    }).then(function(res) {
                        console.log(res.data);
                    });

                    // delete 与 get 类似
                    axios.delete('http://localhost/data?id=4')
                    .then(function(res) {
                        console.log(res.data);
                    });
                    axios.delete('http://localhost/data/5').then(function(res) {
                        console.log(res.data);
                    });
                    axios.delete('http://localhost/data', {
                        params: {
                            id: 6
                        }
                    }).then(function(res) {
                        console.log(res.data);
                    });

                    // put 与 post 类似
                    axios.put('http://localhost/data/7', {
                        uname: '王五',
                        password: '123'
                    }).then(function(res) {
                        console.log(res.data);
                    });

axios 还可以如此传递参数:

axios({
    method: '请求类型',
    url: '请求的URL地址', 
    data: { /* POST数据 */ },
    params: { /* GET参数 */ }
}).then(callback)

axios 的全局配置:

  • axios.defaults.timeout = 3000; // 超时时间

  • axios.defaults.baseURL = 'http://localhost:3000'; // 默认地址

  • axios.defaults.headers['mytoken'] = 'aqwerwqwerqwer2ewrwe23eresdf23'; //设置请求头

  • axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;

  • axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'; // 配置公共的 post 的 Content-Type

                      axios.defaults.baseURL = 'http://localhost';
                      axios.defaults.headers['mytoken'] = 'zhaoliu';
                      axios.get('data-json')
                      .then(function(res) {
                          console.log(res.data);
                      });
    

axios 拦截器

  • 请求拦截器:请求拦截器的作用是在请求发送前进行一些操作,例如在每个请求体里加上token,统一做了处理如果以后要改也非常容易

  • 响应拦截器:响应拦截器的作用是在接收到响应后进行一些操作,例如在服务器返回登录状态失效,需要重新登录的时候,跳转到登录页

                      axios.interceptors.request.use(function(config) {
                          console.log('config: ', config);
                          console.log('url: ', config.url);
                          if (config.url.indexOf('/data-json1') !== -1) {
                              config.headers.mytoken = 'xxxx';
                          }
                          return config
                      });
                      axios.interceptors.response.use(function(res) {
                          console.log('response: ', res);
                          if (res.request.responseURL.indexOf('/data-json1') !== -1) {
                              return res.data;
                          }
                          return res;
                      }, function(err) {
                          console.log('err: ', err);
                      });
                      axios.get('http://localhost/data-json1')
                      .then(function(res) {
                          console.log('data: ', res);
                      });
    

接口调用-async/await用法

async/await是ES7引入的新语法,它的实现是基于Promise,可以更加方便的进行异步操作:

  • async 关键字用于函数上(任何一个 async 函数都会隐式返回一个 promise)

  • await 关键字用于 async 函数当中,await函数不能单独使用(await可以得到异步的结果,也可以直接跟一个 Promise实例对象)

                      async function queryData() {
                          var ret = await axios.get('http://localhost/data/1')
                          return ret.data;
                      }
                      queryData().then(function(data) {
                          console.log('data: ', data);
                      });
                      
                      async function queryData() {
                          var ret = await new Promise(function(resolve, reject){
                              setTimeout(() => {
                                  resolve('100');
                              }, 1000);
                          });
                          return ret;
                      }
                      queryData().then(function(res) {
                          console.log('res: ', res);
                      });
    

async/await 让异步代码看起来、表现起来更像同步代码,多个异步请求的场景:

                    async function queryData() {
                        var info = await axios.get('http://localhost/data/1');
                        var ret = await axios.get('http://localhost/data?id=' + info.data.id);
                        return ret.data;
                    }
                    queryData().then(function(data) {
                        console.log('data: ', data);
                    })

综合案例------(基于后台接口的图书管理案例)

axios 全局配置以及配置响应拦截器:

        // 全局配置 默认地址
        axios.defaults.baseURL = 'http://localhost:80/';
        // 响应拦截器
        axios.interceptors.response.use(function(res) {
            return res.data;
        });

加载图书列表

使用 async、await 封装 queryData 函数获取后台数据:

                queryData: async function() {
                    this.books = await axios.get('books')
                }

删除 mounted 钩子函数中的假数据,调用获取后台接口数据的函数 queryData:

            mounted: function() {
                // 该生命周期钩子函数被触发的时候,模板已经可以使用
                // 一般此时用于获取后台数据,然后把数据填充到模板

                this.queryData();
            }

其他功能实现

验证图书名是否可用:

            watch: {
                bname: async function(n) {
                    if (n.length > 0) {
                        let ret = await axios.get('books/check/' + n);
                        console.log('ret: ', ret);
                        this.confirm = !ret.status;
                    } else {
                        this.confirm = false;
                    }
                }
            }

去编辑图书:

                editHandle: async function(m) {
                    let ret = await axios.get('books/' + m.id);
                    if (ret !== null) {
                        this.bid = ret.id;
                        this.bname = ret.name;
                        this.flag = false;
                    }
                }

添加、编辑图书:

                addHandle: async function() {
                    console.log('addHandle');
                    if (this.flag) { // 添加
                        let ret = await axios.post('books', {
                            name: this.bname
                        })
                        // 成功重新加载数据
                        if (ret.code == 200) {
                            this.queryData();
                        }
                    } else { // 编辑
                        let ret = await axios.put('books', {
                            id: this.bid,
                            name: this.bname
                        });
                        // 成功重新加载数据
                        if (ret.code == 200) {
                            this.queryData();
                        }
                        this.flag = true;
                    }
                    this.bid = '';
                    this.bname = '';
                }

删除图书:

                deleteHandle: async function(m) {
                    let ret = await axios.delete('books/' + m.id);
                    // 成功重新加载数据
                    if (ret.code == 200) {
                        this.queryData();
                    }
                }

图书管理后台实现

新建后台项目文件夹 books, 安装 express、cros 这两个包。

新建 handle.js 文件处理接口:

// 处理文件
const path = require('path');
const fs = require('fs');
const data = require('./data.json');

// 生成图书id
function generateBookId() {
    return Math.max.apply(null, data.map(function(v) {
        return v.id;
    })) + 1;
}
// 写入数据
function writeData(res) {
    fs.writeFile(path.join(__dirname, 'data.json'), JSON.stringify(data), function(err) {
        if (err) {
            res.json({
                code: 500,
                err: err
            });
        } else {
            res.json({
                code: 200,
                msg: '成功'
            });
        }
    })
}
// 获取图书
function books(req, res) {
    res.send(data);
}
// 添加图书
function addBook(req, res) {
    const body = req.body;
    const book = {
        id: generateBookId(),
        name: body.name,
        date: + new Date()
    };
    console.log('book: ', book);
    data.unshift(book);
    writeData(res);
}
// 获取单个图书
function oneBook(req, res) {
    console.log('params: ', req.params);
    const id = req.params.id;
    let book = null;
    data.some(function(v, i, a) {
        console.log('v: ', v);
        if (v.id == id) {
            book = v;
            return true;
        }
    });
    console.log('book: ', book);
    res.json(book);
}
// 编辑图书
function editBook(req, res) {
    const body = req.body;
    console.log('body: ', body);
    data.some(function(v, i, a) {
        if (body.id == v.id) {
            v.name = body.name;
        }
    });
    writeData(res);
}
// 删除图书
function deleteBook(req, res) {
    console.log('params: ', req.params);
    const id = req.params.id;
    data.some((v, i, a) => {
        if (v.id == id) {
            data.splice(i, 1);
        }
    });
    writeData(res);
}
// 验证图书名是否存在
function checkName(req, res) {
    const name = req.params.name;
    let result = false;
    data.some(function(v, i, a) {
        result = name == v.name;
        return result;
    });
    res.json({
        status: result
    });
}

module.exports = {
    books,
    addBook,
    oneBook,
    editBook,
    deleteBook,
    checkName
}

新建 router.js 文件,处理路由:

// 路由模块
let express = require('express');
let router = express.Router();
let handle = require('./handle');

// 获取图书
router.get('/books', handle.books);

// 添加图书
router.post('/books', handle.addBook);

// 获取单个图书
router.get('/books/:id', handle.oneBook);

// 编辑图书
router.put('/books', handle.editBook);

// 删除图书
router.delete('/books/:id', handle.deleteBook);

// 验证图书名是否存在
router.get('/books/check/:name', handle.checkName);

module.exports = router;

新建 index.js 文件,处理服务:

// 导入 express
const express = require('express');

// 创建服务器
const app = express();

// 配置 cors 跨域中间件
const cors = require('cors');
app.use(cors());
// 配置解析 application/x-www-form-urlencoded 格式的表单数据的中间件
app.use(express.urlencoded({ extended: false }));
// 配置解析表单中的 JSON 格式的数据的中间件
app.use(express.json());

// 启动静态资源服务
app.use(express.static('public'))

// 配置路由
let router = require('./router');
app.use(router);

// 指定端口,启动服务器
app.listen(80, function(){
    console.log('server running at http://127.0.0.1:80');
});

附录

附录一

示例 node 后台代码:

// 导入模块
const express = require('express');
// 创建服务器
const app = express();

// 配置跨域中间件
app.use(function(req, res, next) {
    res.header("Access-Control-Allow-Origin", "*");
    res.header('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS');
    res.header('Access-Control-Allow-Headers', 'Content-Type');
    next();
});
// 配置自定义请求头
app.use('/data-json', function(req, res, next) {
    res.header('Access-Control-Allow-Headers', 'mytoken');
    next();
});
app.use('/data-json1', function(req, res, next) {
    res.header('Access-Control-Allow-Headers', 'mytoken');
    next();
});

// 配置数据解析中间件
app.use(express.json());
app.use(express.urlencoded({ extended: false }));

// 监听请求
app.get('/data1', function(req, res) {
    console.log('----data1----');
    res.send('data1');
});
app.get('/data2', function(req, res) {
    console.log('----data2----');
    res.send('data2');
});
app.get('/data3', function(req, res) {
    console.log('----data3----');
    res.send('data3');
});

app.get('/data', function(req, res) {
    console.log(req.query);
    res.send({
        method: 'get',
        ...req.query
    });
});
app.get('/data/:id', function(req, res) {
    console.log(req.params);
    res.send({
        method: 'get',
        ...req.params
    });
});
app.post('/data', function(req, res) {
    console.log(req.body);
    res.send({
        method: 'post',
        ...req.body
    });
});
app.delete('/data', function(req, res) {
    console.log(req.params);
    res.send({
        method: 'delete',
        ...req.query
    });
});
app.delete('/data/:id', function(req, res) {
    console.log(req.params);
    res.send({
        method: 'delete',
        ...req.params
    });
});
app.put('/data/:id', function(req, res) {
    console.log(req.params);
    console.log(req.body);
    res.send({
        method: 'put',
        id: req.params.id,
        ...req.body
    });
});

app.get('/data-json', (req, res) => {
    console.log(req.headers);
    res.json({
        uname: '赵六',
        age: 12
    });
});
app.get('/data-json1', (req, res) => {
    console.log(req.headers);
    res.json({
        uname: '赵六',
        age: 12
    });
});

// 调用 app.listen(端口号, 启动成功后的回调函数),启动服务器
app.listen(80, function() {
    console.log('server running at http://127.0.0.1:80');
});
相关推荐
Yaml49 小时前
Spring Boot 与 Vue 共筑二手书籍交易卓越平台
java·spring boot·后端·mysql·spring·vue·二手书籍
清灵xmf12 小时前
在 Vue 中实现与优化轮询技术
前端·javascript·vue·轮询
前端李易安16 小时前
手写一个axios方法
前端·vue.js·axios
琴~~2 天前
前端根据后端返回的文本流逐个展示文本内容
前端·javascript·vue
程序员徐师兄2 天前
基于 JavaWeb 的宠物商城系统(附源码,文档)
java·vue·springboot·宠物·宠物商城
shareloke2 天前
让Erupt框架支持.vue文件做自定义页面模版
vue
前端李易安2 天前
如何封装一个axios,封装axios有哪些好处
前端·vue.js·axios
你白勺男孩TT3 天前
Vue项目中点击按钮后浏览器屏幕变黑,再次点击恢复的解决方法
vue.js·vue·springboot
虞泽3 天前
鸢尾博客项目开源
java·spring boot·vue·vue3·博客
工业互联网专业3 天前
Python毕业设计选题:基于django+vue的4S店客户管理系统
python·django·vue·毕业设计·源码·课程设计