Vue数据请求

2022-04-19  本文已影响0人  Imkata

1 - 前端接口调用方式

2 - 接口调用-fetch API

<script type="text/javascript">
  /*
    Fetch API 基本用法
      fetch(url).then()
    第一个参数请求的路径,Fetch会返回Promise,所以我们可以使用then拿到请求成功的结果 
  */
  fetch('http://localhost:3000/fdata').then(function(data){
    // text()方法属于fetchAPI的一部分,它返回一个Promise实例对象,用于获取后台返回的数据
    return data.text();
  }).then(function(data){
    //   在这个then里面我们能拿到最终的数据  
    console.log(data);
  })
</script>

1. fetch API 中的 HTTP 请求

<script type="text/javascript">
    /*
          Fetch API 调用接口传递参数
    */
   #1.1 GET参数传递 - 传统URL 通过url ?的形式传参 
    fetch('http://localhost:3000/books?id=123', {
          # get 请求可以省略不写 默认的是GET 
            method: 'get'
        })
        .then(function(data) {
          # 它返回一个Promise实例对象,用于获取后台返回的数据
            return data.text();
        }).then(function(data) {
          # 在这个then里面我们能拿到最终的数据  
            console.log(data)
        });

   #1.2  GET参数传递 - restful形式的URL,通过 / 的形式传递参数,即id = 456,id后台的配置有关   
    fetch('http://localhost:3000/books/456', {
          # get 请求可以省略不写 默认的是GET 
            method: 'get'
        })
        .then(function(data) {
            return data.text();
        }).then(function(data) {
            console.log(data)
        });

   #2  DELETE请求方式参数传递,删除id=789的书籍
    fetch('http://localhost:3000/books/789', {
            method: 'delete'
        })
        .then(function(data) {
            return data.text();
        }).then(function(data) {
            console.log(data)
        });

   #3.1 POST请求传参,参数使用&分隔的字符串
    fetch('http://localhost:3000/books', {
            method: 'post',
          # 传递数据 
            body: 'uname=lisi&pwd=123',
          # 设置请求头 
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded'
            }
        })
        .then(function(data) {
            return data.text();
        }).then(function(data) {
            console.log(data)
        });

   #3.2 POST请求传参,JSON格式的
    fetch('http://localhost:3000/books', {
            method: 'post',
            // 将对象转化成json形式的字符串
            body: JSON.stringify({
                uname: '张三',
                pwd: '456'
            }),
            headers: {
                'Content-Type': 'application/json'
            }
        })
        .then(function(data) {
            return data.text();
        }).then(function(data) {
            console.log(data)
        });

    #4 PUT请求传参,修改id是123的书籍
    fetch('http://localhost:3000/books/123', {
            method: 'put',
            body: JSON.stringify({
                uname: '张三',
                pwd: '789'
            }),
            headers: {
                'Content-Type': 'application/json'
            }
        })
        .then(function(data) {
            return data.text();
        }).then(function(data) {
            console.log(data)
        });
</script>

2. fetch API 中的响应格式

用fetch来获取数据,如果响应正常返回,我们首先看到的是一个response对象,其中包括返回的一堆原始字节,这些字节需要在收到后,需要我们通过调用方法将其转换为相应格式的数据,比如JSON或者TEXT等等。

/*
  Fetch响应结果的数据格式
*/
fetch('http://localhost:3000/json').then(function(data){
  return data.json();    //  将获取到的数据,转换成json对象
  // return data.text(); //  将获取到的数据,转换成字符串 
}).then(function(data){
  // 如果返回的是data.text(),需要使用JSON.parse()转换成json对象
  // var obj = JSON.parse(data); 
  // console.log(obj.uname);
  
  // 如果返回的是data.json(),就不需要解析成json对象了,可以直接用 
  console.log(data.uname,data.age,data.gender)
})

3 - 接口调用-axios 第三方库 (推荐)

axios是一个基于Promise用于浏览器和node.js的http客户端。

1. axios 的常用API

2. axios 参数传递

① get 和 delete 请求传递参数

# 1. 发送get 请求 
  axios.get('http://localhost:3000/adata').then(function(ret){ 
    #  拿到的 ret 是一个对象,返回的数据存在 ret 的data 属性里面
    console.log(ret.data)
  })
# 2.  get 请求传递参数
  # 2.1  通过传统的url  以 ? 的形式传递参数
  axios.get('http://localhost:3000/axios?id=123').then(function(ret){
    console.log(ret.data)
  })
  # 2.2  restful 形式传递参数 
  axios.get('http://localhost:3000/axios/123').then(function(ret){
    console.log(ret.data)
  })
  # 2.3  通过params形式传递参数(推荐),第一个参数是url地址,第二个参数是携带的参数
  axios.get('http://localhost:3000/axios', {
    params: {
      id: 789
    }
  }).then(function(ret){
    console.log(ret.data)
  })
# 3 axios delete 请求传参,传参的形式和get请求一样,这里只演示一种
  axios.delete('http://localhost:3000/axios', {
    params: {
      id: 111
    }
  }).then(function(ret){
    console.log(ret.data)
  })

② post 和 put 请求传递参数

# 4  axios 的 post 请求
  # 4.1 通过params形式传递参数,默认传递的是json对象 (推荐)
  axios.post('http://localhost:3000/axios', {
    uname: 'lisi',
    pwd: 123
  }).then(function(ret){
    console.log(ret.data)
  })
  # 4.2 通过 URLSearchParams 传递参数,这种类型的参数是通过&和=隔开的键值对(表单形式)传递的 
  var params = new URLSearchParams();
  params.append('uname', 'zhangsan');
  params.append('pwd', '111');
  axios.post('http://localhost:3000/axios', params).then(function(ret){
    console.log(ret.data)
  })

# 5  axios put 请求传参,传参的形式和post请求一样,这里只演示一种
  axios.put('http://localhost:3000/axios/123', {
    uname: 'lisi',
    pwd: 123
  }).then(function(ret){
    console.log(ret.data)
  })

总结:为了保持统一,我们都使用params形式传递参数,默认传递的是json对象。

3. axios 的响应结果

响应结果的主要属性:

axios.post('http://localhost:3000/axios', {
  uname: 'lisi',
  pwd: 123
}).then(function(res){
  console.log(res.data) //实际响应回来的数据
  console.log(res.headers) //响应头信息
  console.log(res.status) //响应状态码
  console.log(res.statusText) //响应状态信息
})

4. axios 全局配置

# 配置请求的基准URL地址
axios.defaults.baseURL = 'https://api.example.com';
# 配置超时时间
axios.defaults.timeout = 2500;
# 配置请求头信息,例如:将登陆之后的token传进去(对于跨域访问服务,需要在后台设置)
axios.defaults.headers['mytoken'] = 'asduifayvuarsvafjfd';

// 下面两种用的比较少
# 配置公共的请求头
axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
# 配置公共的 post 的 Content-Type
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';

5. axios 拦截器

① 请求拦截器

请求拦截器的作用是在请求发送前进行一些操作。

例如,在每个请求体里加上token,统一做了处理如果以后要改也非常容易。interceptors n. 拦截器

# 1. 请求拦截器 
axios.interceptors.request.use(function(config) {
    console.log(config.url)     //请求地址
    console.log(config.method)  //请求方法
    console.log(config.data)    //请求参数
    console.log(config.headers) //请求头
    # 1.1 任何请求都会经过这一步,在发送请求之前做些什么   
    config.headers.mytoken = 'nihao';
    # 1.2 这里一定要return,否则配置不成功  
    return config;
  }, function(err){
    # 1.3 对请求错误做点什么    
    console.log(err)
  })

② 响应拦截器

响应拦截器的作用是在接收到响应后进行一些操作。

例如,在服务器返回登录状态失效,需要重新登录的时候,跳转到登录页。

# 2. 响应拦截器 
axios.interceptors.response.use(function(res) {
  // console.log(res)
  # 2.1 在接收响应做些什么,这里是将响应对象的data直接返回出去,这样以后拿到的就直接是data了,当然缺点就是res里面的其他东西也被拦截掉了,所以不推荐
  var data = res.data;
  return data;
}, function(err){
  # 2.2 对响应错误做点什么  
  console.log(err)
})

4 - 如何解决回调地狱的问题

如果有需求:依次读取A文件、B文件、C文件,如果进行函数嵌套实现,会有回调地狱的问题,怎么解决呢?

1. 使用Promise解决(ES6新增)

Promise出现的目的是解决Node.js异步编程中回调地狱的问题。

我们使用new来构建一个Promise,Promise的构造函数接收一个参数,是函数,并且传入两个参数: resolve,reject,分别表示异步操作执行成功后的回调函数和异步操作执行失败后的回调函数。

let promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        if (true) {
            resolve({name: '张三'})
            // 在里面通过resolve把成功的结果传出去
        } else {
            reject('失败了')
            // 在里面通过reject把失败的结果传出去
        }
    }, 2000);
});
//在外面通过调用.then传入一个函数从而拿到成功的结果,通过调用.catch传入一个函数拿到失败的结果
promise.then(result => console.log(result); // {name: '张三'}
       .catch(error => console.log(error);  // 失败了

Promise使用举例,代码如下:

const fs = require('fs');

// 回调地狱的问题
// fs.readFile('./1.txt', 'utf8', (err, result1) => {
//     console.log(result1)
//     fs.readFile('./2.txt', 'utf8', (err, result2) => {
//         console.log(result2)
//         fs.readFile('./3.txt', 'utf8', (err, result3) => {
//             console.log(result3)
//         })
//     })
// });

// 使用Promise解决回调地狱的问题
function p1 () {
    return new Promise ((resolve, reject) => {
        fs.readFile('./1.txt', 'utf8', (err, result) => {
            resolve(result)
        })
    });
}

function p2 () {
    return new Promise ((resolve, reject) => {
        fs.readFile('./2.txt', 'utf8', (err, result) => {
            resolve(result)
        })
    });
}

function p3 () {
    return new Promise ((resolve, reject) => {
        fs.readFile('./3.txt', 'utf8', (err, result) => {
            resolve(result)
        })
    });
}

// p1()返回的是个Promise对象,它会自动调用下一个then,然后把结果放到 then 的参数里面
p1().then((r1)=> { // 这里的r1就是读取文件1的返回值
    console.log(r1);
    return p2(); // 返回的还是一个Promise对象
})
// 上个Promise对象会自动调用then,然后把结果放到 then 的参数里面
.then((r2)=> { // 这里的r2就是读取文件2的返回值
    console.log(r2);
    return p3(); // 返回的还是一个Promise对象
})
// 上个Promise对象会自动调用then,然后把结果放到 then 的参数里面
.then((r3) => { // 这里的r3就是读取文件3的返回值
    console.log(r3)
})

① 对象方法: .then() .catch() .finally()

对象方法是通过Promise实例对象调用的。

<script type="text/javascript">
  // Promise常用API-对象方法
  // console.dir(Promise);
  function foo() {
    return new Promise(function(resolve, reject){
      setTimeout(function(){
        // resolve(123);
        reject('error');
      }, 100);
    })
  }
  
  // 错误信息写在.catch里面
  foo()
    .then(function(data){ //异步任务正确的结果
       console.log(data)
    })
    .catch(function(err){ //错误信息
        console.log(err)
    })
    .finally(function(){  //成功与否都会执行(不是正式标准) 
        console.log('finished')
    });

</script>

② 类方法: .all() race()

类方法是直接通过Promise调用的。

<script type="text/javascript">
  /*
    Promise常用API-类方法
  */
  // console.dir(Promise)
  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){
     // all 中的参数  [p1,p2,p3] 和 返回的结果一一对应[" HELLO TOM", "HELLO JERRY", "HELLO SPIKE"]
     console.log(result) // ["HELLO TOM", "HELLO JERRY", "HELLO SPIKE"]
   })
  Promise.race([p1,p2,p3]).then(function(result){
    // 由于p1执行较快,Promise的then()将获得结果'P1'。p2,p3仍在继续执行,但执行结果将被丢弃。
    console.log(result) // "HELLO TOM"
  })
</script>

2. 使用异步函数解决(ES7新增,推荐)

异步函数是异步编程语法的终极解决方案,它是基于promise对象上的一层封装,它可以让我们将异步代码写成同步的形式,让代码不再有回调函数嵌套,使代码变得清晰明了。

// 匿名函数
const fn = async () => {};

// 命名函数
async function fn () {}

① async关键字

  1. 普通函数定义前加async关键字,普通函数变成异步函数,异步函数默认返回promise对象
  2. 在异步函数内部使用return关键字进行结果返回,结果会被包裹在promise对象中,return关键字代替了resolve方法。在异步函数内部使用throw关键字抛出程序异常,throw关键字代替了reject方法
  3. 调用异步函数再链式调用then方法获取异步函数执行结果,调用catch方法获取异步函数执行的错误信息
# 1. async 基础用法
# 1.1 async作为一个关键字放到函数前面
async function queryData() {
  # 1.2 await关键字只能在使用async定义的函数中使用,await后面只能跟promise对象
  var ret = await new Promise(function(resolve, reject){
    setTimeout(function(){
      resolve('nihao')
    },1000);
  })
  return ret;
}
# 1.3 任何一个async函数都会隐式返回一个promise,我们可以使用then进行链式编程
queryData().then(function(data){
  console.log(data)
})

② await关键字

  1. await关键字只能出现在异步函数中
  2. await promise,await后面只能写promise对象,写其他类型的API是不可以的
  3. await关键字可以暂停异步函数的执行,等待promise对象返回结果后再向下执行函数,有了await关键字我们可以将异步代码写成同步的形式,这样就不再需要.then和回调函数了
# 默认的axios请求,我们需要在then里面获取请求结果
axios.defaults.baseURL = 'http:localhost:3000';
axios.get('adata').then(function(ret){
  console.log(ret.data)
})
async function queryData() {
  # 如果加上await,我们就能直接拿到请求结果了,不用使用then
  var ret = await axios.get('adata'); 
  return ret.data;
}
# async函数的返回值是个Promise实例对象,我们使用then获取数据
queryData().then(function(data){
  console.log(data) // 这里我们就能直接获取data了
})

③ async和await处理多个异步函数

# 2. async 函数处理多个异步函数
axios.defaults.baseURL = 'http://localhost:3000';

async function queryData() {
  # 2.1 添加await之后,当前的await返回结果之后才会执行后面的代码,这样就可以将info当做参数传到下一个接口中
  var info = await axios.get('async1');
  # 2.2 有了await,我们不再需要then,也不再需要回调函数,这种代码的风格让异步代码看起来更像同步代码
  // 后面的请求需要使用前面请求的结果
  var ret = await axios.get('async2?info=' + info.data); 
  return ret.data;
}

queryData().then(function(data){
  console.log(data)
}) 

总结:上面就是async和await最经典的使用,使用async就是为了使用.then获取结果,使用await就是为了异步代码写成同步的形式。

5 - 图书管理案例 - 基于接口重构

上面的 图书管理案例 都是死数据,现在我们基于接口重构一下。

1. 通过接口获取图书列表

<div id="app">
    <div class="grid">
        <table>
            <thead>
                <tr>
                    <th>编号</th>
                    <th>名称</th>
                    <th>时间</th>
                    <th>操作</th>
                </tr>
            </thead>
            <tbody>
                # 5 把book中的数据渲染到页面上
                <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>
</div>
<script type="text/javascript" src="js/vue.js"></script>
# 1 导入axios   
<script type="text/javascript" src="js/axios.js"></script>
<script type="text/javascript">
    /*
         图书管理-添加图书
     */
    # 2 配置公共的url地址,简化后面的调用方式
    axios.defaults.baseURL = 'http://localhost:3000/';
    // 响应拦截器
    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: {
            # 3 定义一个方法,用来发送请求
            #   使用 async 让函数变成异步函数,从而可以使用.then
            queryData: async function() {
                // 调用后台接口获取图书列表数据
                #  使用 await 让异步代码写成同步的形式。
                this.books = await axios.get('books'); 
                // 因为上面加了响应拦截器,所以这里直接获取的就是data
            }
        },
        # 4 mounted里面,DOM已经加载完毕,在这里调用函数 
        mounted: function() {
            this.queryData();
        }
    });
</script>

2. 添加图书

methods: {
  // 在func前面添加async
  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 {
          # 1  在以前封装好的 handle 方法中,发送请求  
          # 2  使用 async 和 await 简化操作,需要在 function 前面添加 async   
          var ret = await axios.post('books', {
            name: this.name
          })
          # 3  根据后台返回的状态码判断是否加载数据 
          if(ret.status == 200) {
            # 4  调用 queryData 这个方法,渲染最新的数据 
            this.queryData();
          }
        }
        // 清空表单
        this.id = '';
        this.name = '';
      },        
}         

3. 验证图书名称是否存在

watch: {
      name: async function(val) {
        // 验证图书名称是否已经存在
        // var flag = this.books.some(function(item){
        //   return item.name == val;
        // });
        
        # 1 使用 async 和 await 简化操作,需要在 function 前面添加 async   
        var ret = await axios.get('/books/book/' + this.name);
        # 2 根据后台返回的状态码判断是否加载数据 
        if(ret.status == 200) {
          // 图书名称存在
          this.submitFlag = true;
        }else{
          // 图书名称不存在
          this.submitFlag = false;
        }
      }
},

4. 编辑图书

methods: {
      handle: async function(){
        if(this.flag) {
          # 3 编辑图书,把用户输入的信息提交到后台
          var ret = await axios.put('books/' + this.id, {
            name: this.name
          });
          if(ret.status == 200){
            # 4 完成添加后,重新加载列表数据
            this.queryData();
          }
          this.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){
        # 1 flag状态位用于区分编辑和添加操作
        this.flag = true;
        # 2 根据id查询出对应的图书信息
        var ret = await axios.get('books/' + id);
        this.id = ret.id;
        this.name = ret.name;
      },

5. 删除图书

deleteBook: async function(id){
      // 删除图书
      var ret = await axios.delete('books/' + id);
      if(ret.status == 200) {
        // 重新加载列表数据
        this.queryData();
      }
}
上一篇下一篇

猜你喜欢

热点阅读