8、数据交互 axios

一、axios介绍

  • 官网:http://www.axios-js.com/
  • 看云:https://www.kancloud.cn/yunye/axios/234845(文档托管平台,有axios的详细使用)
  • 概念
     axios是基于Promise创建的http库,可以用于客户端(浏览器)和node.js
     let p1 = new Promise((resolve,reject)=>{
         //异步代码
         resolve(参数),
         reject(错误信息)
     })
     p1.then((res)=>{
         console.log(res);//成功的回调,调用resolve,通过res获取到传递过来的数据
     }).catch((err)=>{
         console.log(err); //失败的回调,调用reject,err错误信息
     })
    
    官方概念:
     Axios是一个基于promise的HTTP库,可以在浏览器和node.js中使用
    
  • 特点
    • 从浏览器中创建XMLHttpRequests
    • 从node.js中创建http请求
    • 支持Promise API
    • 拦截请求和响应
    • 转换请求数据和响应数据
    • 取消请求
    • 自动转换JSON数据
    • 客户端支持防御[XSRF]
     跨站请求伪造(英语:Cross-site request forgery),也被称为 one-click attack
     或者 session riding,通常缩写为 CSRF 或者 XSRF, 是一种挟制用户在当前已登录的Web
     应用程序上执行非本意的操作的攻击方法。跟跨网站脚本(XSS)相比,XSS 利用的是用户对指定
     网站的信任,CSRF 利用的是网站对用户网页浏览器的信任。
    
  • 下载(1的版本有问题,不稳定)
    npm i axios@0
    
    "axios": "^0.27.2
    

二、 axios基本语法

  • 引入axios库文件,它会暴露一个==axios()方法,这个方法下面还有axios.get()axios.post()==两个方法

2、1 axios({})

  • 类似于$.ajax(),可以发起get或者post请求,返回promise对象
axios({
   url: '你要请求的接口地址',
   method: 'get/post', // 默认get// get传参   
   params: {
       // 你要传入的get方式请求的入参
    },
   // post传参
   data: {
      // 你要传入的post方式请求的入参
   }
 }).then((res)=>{
     // 成功时候的响应res,axios有一个特点,返回的数据包含很多的配置信息
     // http状态码是200或者304,执行成功的逻辑(我们关心的是后端返回的res.code)
 }).catch((err)=>{
    // 错误时候的响应err
    // http状态码非200或者304,执行错误的逻辑
 })

2、2 axios.get(url,{})

  • 类似于$.get(),发起get请求,注意传参比较特别
     axios.get('你要请求的接口地址', {
         params: {
              // 你要传入的get方式请求的入参,如:
              a:1,
              b:2
         }
     }).then((res)=>{
          // 成功时候的响应res,axios有一个特点,返回的数据包含很多的配置信息
         // http状态码是200或者304,执行成功的逻辑
     }).catch((err)=>{
        // 错误时候的响应err
        // http状态码非200或者304,执行错误的逻辑  
     })
    

2、3 axios.post(url,{})

  • 类似于$.post(),发起post请求
    axios.post('你要请求的接口地址', {
      // 你要传入的post方式请求的入参,如:
       a:1,
       b:2
    }).then((res)=>{
      // 成功时候的响应res,axios有一个特点,返回的数据包含很多的配置信息
      // http状态码是200或者304,执行成功的逻辑
    }).catch((err)=>{
      // 错误时候的响应err
      // http状态码非200或者304,执行错误的逻辑
    })
    
    除了data是真正后端返回的,其他的都是axios配置的
    在这里插入图片描述

三、 基本案例

3、1 局部调用axios

  • 代码
<template>
  <div>
    <h1>this is axios--局部使用</h1>
    <ul>
      <li v-for="item in datalist" :key="item.id">{{ item.name }}</li>
    </ul>
  </div>
</template>

<script>
//1.下载 npm i axios@0
//2.引入 (在那使用,在那引入--局部)
import axios from "axios";
export default {
  data() {
    return {
      datalist: [],
    };
  },

  //在created,beforeMount,mounted都可以发起ajax请求,一般在mounted发起ajax请求
  mounted() {
    //3.请求数据 axios({url,method,params:{}/data:{}})
    // axios({
    //   url: "http://jsonplaceholder.typicode.com/comments", //请求地址
    //   method: "get", //请求方式 get和post  默认就是get
    //   params: { postId: 1 }, //get请求带参数,放在params中
    // })
    //   .then((res) => {
    //     //请求成功
    //     console.log("请求成功");
    //     //res会有很多的配置信息,但是真正的数据在data属性中
    //     console.log(res.data);

    //     //4.请求到数据,赋值,渲染到页面
    //     this.datalist = res.data;
    //   })
    //   .catch((err) => {
    //     //请求失败
    //     console.log("请求失败");
    //     console.log(err); //错误信息
    //   });

    //4.axios.get(url,{params:{}})
    axios
      .get("http://jsonplaceholder.typicode.com/comments", {
        params: {
          postId: 1,
        },
      })
      .then((res) => {
        //请求成功
        console.log("请求成功");
        //res会有很多的配置信息,但是真正的数据在data属性中
        console.log(res.data);

        //4.请求到数据,赋值,渲染到页面
        this.datalist = res.data;
      })
      .catch((err) => {
        //请求失败
        console.log("请求失败");
      });
  },
};
</script>

<style>
</style>

3、2 全局调用axios

  • 代码
    定义成全局的 在main.js中
import Vue from 'vue'
import App from './App.vue'
import router from './router'

Vue.config.productionTip = false

//1.全局引入axios
import axios from "axios";
//2.添加到vue原型上
Vue.prototype.$axios = axios;

new Vue({
  router,
  render: h => h(App)
}).$mount('#app')

在案例的vue文件中

<template>
 <div>
   <h1>this is axios -- 全局使用</h1>
   <ul>
     <li v-for="item in datalist" :key="item.id">{{ item.name }}</li>
   </ul>
 </div>
</template>

<script>
//基本上所有的组件都可能会有数据请求,如果每次使用都引入一次,麻烦
//定义成全局的
//1.全局引入axiox import axios from "axios";
//2.添加到vue原型上  Vue.prototype.$axios = axios;
//3.在组件中使用
export default {
   data() {
   return {
     datalist: [],
   };
 },
 mounted() {
   this.$axios({
     url: "http://jsonplaceholder.typicode.com/comments", //请求地址
     method: "get", //请求方式 get和post  默认就是get
     params: { postId: 1 }, //get请求带参数,放在params中
   })
     .then((res) => {
       //请求成功
       console.log("请求成功");
       //res会有很多的配置信息,但是真正的数据在data属性中
       console.log(res.data);

       //4.请求到数据,赋值,渲染到页面
       this.datalist = res.data;
     })
     .catch((err) => {
       //请求失败
       console.log("请求失败");
       console.log(err); //错误信息
     });
 },
};
</script>

<style>
</style>

四、如何解决跨域问题

注意:开发阶段的跨域是前端自己解决,生产环境下的跨域后端解决

  • vue的配置文档:https://cli.vuejs.org/zh/config
  • 创建一个vue的全局配置 vue.config.js文件(在这里写node的语法),一定要和package.json同级才生效
  • 注意:配置文件一旦修改必须要重启前端

4、1方式一(简单)

  • vue.config.js
// 把你封装的所有配置文件全部导出去
module.exports = {
   // publicPath 可以根据你不同的环境去配置它的初始地址,一般不会修改我们就'/'
   // outputDir 输出目录,用于打包。执行npm run build 默认生成dist文件夹,如果你要修改成其他文件夹名就直接设置type类型

   // devServer 本地开发服务配置
   devServer:{
       // 设置你的代理地址(这里写后端服务器的地址)
       // 以后前端访问服务器地址时,前面直接写 /
       // 后端地址本来是:http://localhost:3000/api/getbanner
       // 会换成:http://localhost:8080/api/getbanner
       // 这样就没有跨域问题了
       // proxy即代理
       proxy: 'http://localhost:3000'
    }
  }
  • 说明
    • 优点:配置简单,请求资源时直接发给前端(8080)即可。
    • 缺点: 不能配置多个代理,不能灵活的控制请求是否走代理(如果前端public下有资源,会走前端自己的这个public下的资源,即public为根路径)
    • 工作方式: :若按照上述配置代理,当请求了前端不存在的资源时,那么该请求才会转发给服务器 (优先匹配前端public下的资源)
  • 操作步骤:
1.配置vue.config.js
2.请处理`http://localhost:3000/api/getbanner` 换成 `/api/getbanner`
3.重启前端

如果修改了配置文件,前端一定要重启

4、1方式二(推荐)

  • vue.config.js
  module.exports = {
      devServer: {
         proxy: {
         '/api1': { // 匹配所有以'/api1'开头的请求路径。以/api1开头,是指端口后面是/api1,再接正常路径。/api1/api/getbanenr ==> /api/getbanenr
             target: 'http://localhost:3000', // 代理目标的基础路径
             changeOrigin: true,
             secure: false, // 默认情况下,将不接受在 HTTPS 上运行且证书无效的后端服务器。 如果需要,可以这样修改配置
             pathRewrite: {'^/api1': ''} // 后端收到的请求地址中,将'/api1'替换为''
             //api/getbanner
          },
          '/api2': { // 匹配所有以'/api2'开头的请求路径
             target: 'http://localhost:3001', // 代理目标的基础路径
             changeOrigin: true,
             pathRewrite: {'^/api2': ' '}
          }
       }
    }
 }
 /*
 changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:3000 或 3001
 changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:8080
 changeOrigin默认值为true,在react中默认为false
*/
  • 说明
    • 优点:可以配置多个代理,且可以灵活的控制请求是否走代理
    • 缺点:配置略微繁琐,请求资源时必须加前缀
      module.exports = {
        devServer: {
          proxy: {
             '/ujiuye': {
              // 匹配所有以'/pzh'开头的请求路径。以/pzh开头,是指端口后面是/pzh,再接正常路径
              // 这个路径会走代理:http://localhost:8080/ujiuye/api/getbanner
              // 后端收到的实际是:http://localhost:3000/api/getbanner
              target: 'http://localhost:3000', // 代理目标的基础路径
              changeOrigin: true,
              pathRewrite: { '^/ujiuye': '' }, // 后端收到的请求地址中,将'/pzh'替换为''
             },
          },
       },
     };
    
  • 步骤:
1.配置vue.config.js
2.请处理`http://localhost:3000/api/getbanner`换成`/pzh/api/getbanner`
3.重启前端

环境判断

let baseURL = ''; // 基础地址

// 区分清楚是生产环境还是开发环境console.log(process.env.NODE_ENV); // 返回开发环境和生产环境不同标识
// development(开发环境)
// production(生产环境)

if (process.env.NODE_ENV === 'development') {
    // 开发环境baseURL = '/pzh/api';
} else if (process.env.NODE_ENV === 'production') {
   // 生产环境baseURL = 'http://localhost:3000/api';
}

五、post的入参(请求)格式

  • 主要有一下三种:依据Content-Type来区分
  • 成熟的后端,第一种接收和第二种接收都应该设置好,第三种接收是接收上传文件
  • 普通的方式(传对象)(request payload)
    Content-Type: application/json
    
    即数据以对象的形式传输。或者JSON.stringify()转的json对象字符串
    
    {"phone":"155","password":"111"}
    
  • 一般表单传参(模拟表单)(传查询字符串)(form-data)比较多用
    注意点:它属于表单格式传参中没有上传文件的传输方式
     Content-Type: application/x-www-form-urlencoded
      
     即数据以 'a=1&b=2'  上传
    
  • 带有上传文件的表单传参(传文件)(利用form-data包装数据)
    https://developer.mozilla.org/zh-CN/docs/Web/API/FormData
    Content-Type: multipart/form-data

需要用到原生的FormData函数,将数据转成这种格式(FormData就是包装上传数据的一个容器)

// 语法
let file = new FormData(); // file 就是数据对象
// 添加数据file.append('key','value')
// 取值数据file.get('key')


==============================
// 如何将对象中的数据添加到FormData中呢,需要循环遍历
let file = new FormData()
for(let attr in 入参对象){
   file.append(attr, 入参对象[attr])
}
总结:
前端不用手动设置Content-Type,但是传参的数据格式不一样,默认Content-Type也是不一样的
 1、普通的方式,即传入一个对象    Content-Type: application/json
 
 2、一般表单传参,即传入查询字符串(推荐)   Content-Type: application/x-www-formurlencoded
 3、带有上传文件的表单传参,即传FormData对象    Content-Type: multipart/form-data
  • 第三方包qs进行数据格式转换
    思考:我们的数据一般都是对象,如何将对象数据转成查询字符串这种格式
    方式一:
    利用原生node.js方法中querystring去转化
    
    querystring.stringify({ foo: 'bar', baz: ['qux', 'quux'], corge: '' });
    // 返回 'foo=bar&baz=qux&baz=quux&corge='
    
    方式二:
    qs第三方包(在vue脚手架环境下,默认自带第三方包,直接引过来用即可,不用安装)
    

六、接口的封装

  • 目的:分门别类,好维护,方便管理
  • 概述:所谓的接口封装,就是单独定义出一个模块,专门用于管理整个网站的http请求,而不是每个组件中写请求,这样方便统一管理
  • 步骤
    1.在src下创建request文件夹
    2.request 文件夹下会有`axios.js`和`api.js`两个文件  
    3.axios.js引入axios库并创建http实例,api.js引入axios.js中的 http 实例,然后其它的组件引入api.js中定义的方法
    

6、1 request/axios.js

  • 作用:这个模块用于管理 axios,并设置基础的配置(比如说拦截器等)
  • 注意:vue原型上就不需要再挂载axios了
  • 基础款
    import axios from 'axios'; // 引入axios,因此vue的原型上就不需要axios了
    
    let baseURL = ''; // 基础地址
    if (process.env.NODE_ENV === 'development') {
       // 开发环境
       baseURL = '/ujiuye/api';
    } else if (process.env.NODE_ENV === 'production') {
       // 生产环境
       baseURL = 'http://localhost:3000/api';
    }
    
     // 调用创建新实例
    let http = axios.create({
        // 在这里,定义当前实例的自定义配置
        baseURL: baseURL // 基础地址,它可以统一管理你的接口地址
    });
    
    // 导出新实例
    export default http;
    
  • 完美款
    更完美的写法:加上请求拦截和响应拦截
    import axios from 'axios'; // 引入axios,因此vue的原型上就不需要axios了
    
    let baseURL = ''; // 基础地址
    console.log(process.env.NODE_ENV); // 开发环境和生产环境
    
    if (process.env.NODE_ENV === 'development') {
       // 开发环境,需要做跨域代理
       baseURL = '/ujiuye/api';
     } else if (process.env.NODE_ENV === 'production') 
       // 生产环境,不用做跨域代理
       baseURL = 'http://localhost:3000/api';
     }
    
    // 调用创建新实例
    let http = axios.create({
       // 在这里,定义当前实例的自定义配置
       baseURL: baseURL // 基础地址,它可以统一管理你的接口地址
    });
    
    // 请求拦载  interceptors拦截器    
     http.interceptors.request.use((req) => {
           return req;
     });
     // 响应拦载
     http.interceptors.response.use((res) => {
           return res;
     });
     
    // 导出新实例
    export default http;
    

6、2 request/api.js

  • 作用:所有的请求都在这个模块,方便管理,而不用写在每个组件中
     import http from './axios'; // 引入你封装好的axios实例
     import qs from 'querystring'; // 引入原生node的querystring,用它下面的stringify方法,将{ foo: 'bar', corge: '123' }转换成'foo=bar&corge=123'。也可以用第三方包
     
     // 下面封装一系列接口
    
     // 登录接口,post方式,有参数
     // 上传的数据格式可以用json,也可以改成查询字符串。这里就是改成了查询字符串,这个要看后端接口的需求
     export function login(data) {
        return http.post('/login',qs.stringify(data));
     }
     // 获取banner接口,get方式,没有要传的参数
     export function getBanner() {
        return http.get('/getbanner');
     }
     // 注册接口,post方式,有参数
     export function register(data) {
        return http.post('/register',qs.stringify(data));
     }
    

6、3 使用

  • 从request的api.js中引入需要的方法,在某个组件中去使用
  • 在组件中使用
     <template>
       <div>
          <!-- 标题 -->
          <v-title></v-title>
          <p>电 话:<input type="text" v-model='user.phone'></p>
          <p>昵 称:<input type="text" v-model='user.nickname'></p>
          <p>密 码:<input type="text" v-model='user.password'></p>
    
         <p><button @click='goRegister'>注册</button></p>
       </div>
     </template>
    
     <script>
        import {register} from "@/request/api.js"
        export default {
           data(){
               return {
                  user:{
                     phone:"",
                     nickname:"",
                     password:""
                  }
             }
           },
         methods:{
            goRegister(){
               //1.判断,必须输入了内容才能往下执行
               if(this.user.phone == "" || this.user.password == "" || this.user.nickname == ""){
                   alert("请输入必要内容");
                    return;
                }
    
              //2.注册
              // this.$axios.post("/ujiuye/api/register",this.user)
             register(this.user)
            .then(res=>{
                 if(res.data.code == 200){
                     alert(res.data.msg);
                     this.$router.push("/login")
                 }else{
                     alert(res.data.msg);
                 }
            }).catch(err=>{
               console.log(err);
            })
         }
      }
    }
    </script>
    
    <style scoped>
      p{
        line-height: .8rem;
        text-align: center;
      }
    </style>
    

七、axios的并发处理(axios.all())【了解】

  • 所谓并发,即同时可以调用N个接口
  • 方法:
    axios.all([请求方法1(),请求方法2()])
    .then(res=>{ res中有两次请求的数据 })
    .catch(err=>{ 错误 })
    

可以用axios.spread()展开并发结果。spread的参数为一个回调函数,回调函数的参数即并发调用返回的一一对应的结果

  • //1、单独引入axios库,并发是在某个组件中使用,封装的http上面没有all方法
     import axios from "axios";
    
    //2.引入封装好的接口,这里引了两个接口
    import { getHomeGoods, getHomeCate } from "../request/api;
    
    //3.发请求
    axios
     .all([getHomeCate(), getHomeGoods()])
     .then(
        // spread展开,它接收一个函数,该函数的参数即返回的结果
        // cate即第一个请求的结果,goods即第二个请求的结果
        axios.spread((cate, goods) => {
         // 对第一个处理
         if (cate.data.code == 200) {
              this.catelist = cate.data.list;
         } else {
              alert(cate.data.msg);
         }
         
        // 对第二个处理
        if (goods.data.code == 200) {
             this.hotslist = goods.data.list[0].content;
             this.newlist = goods.data.list[1].content;
             this.goodslist = goods.data.list[2].content;
        } else {
             alert(goods.data.msg);
        }
      })
     }
     // 错误统一处理
     .catch(err => {
          console.log(err, "错误");
      });
    

八、拦截器

都写在request/axios.js中

  import axios from 'axios'; // 引入axios,因此vue的原型上就不需要axios了

 // 调用创建新实例的方法
 let http = axios.create({
      // 在这里,定义当前实例的自定义配置
      baseURL: '/api'   // 基础地址,它可以统一管理你的接口地址
 });
  
  // 请求拦截器:在req.headers中添加属性,一般用于添加token
  http.interceptors.request.use((req) => {
     // req.headers.pzh = 123; // req请求头,在请求头中,添加一个请求属性,因此在请求的网络中,就可以看到此请求属性(一般用于添加token)
     return req;
  });

  // 响应拦截器:1、过滤数据   2、判断token是否过期等
  http.interceptors.response.use((res) => {
  // console.log(res.data);  // 即后端返回来的数据
  // 用法一、你可以全局操作返回的数据。比如后端返回来的数据经过包装,总包含一些不需要的东西,所以我们可以只把需要的返回出去
  // return res.data;

  // 二、全局错误判断(重点)  比如:token过期,如果过期,我们可以让它再到登录去

       return res;
  });

  // 导出

 export default http;

8、1 axios请求拦截和路由导航守卫的区别

axios是发起ajax请求时触发,一般用于添加token,全局错误处理,返回的数据过滤
导航守卫是访问某个路由时触发,一般用于判断是否登录,某个用户是否有访问某个路由的权限等

token的概念

  • token就是令牌的意思。token从哪里产生?后端
  • 实际上每一个必须要登录才能看见界面中的接口,都要验证一次用户名和密码,但是每一次验证都会给服务器造成压力,因此产生了token概念
  • token是前端登录后,后端产生,传递给前端,前端将token存在本地存储中。当前端要访问某些需要登录才能访问的接口时,将它带着,这样后端就只需要验证token,而不用去数据库验证用户名和密码,这样减轻了后端的压力。
  • token会失去它的时效,一般重要的页面3、5分钟
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值