Axios 学习总结

本贴最后更新于 1901 天前,其中的信息可能已经时移世易

一、第 1 章简介

1.1Axios 是什么?

Axios 是一个基于 promise 的 HTTP 库,类似 jquery 中的 ajax
它可以用于浏览器和 node.js 中

1.2Axios 有哪些特性?
  • 支持 Promise API
  • 拦截请求和相应
  • 转换请求数据和相应数据
  • 取消请求
  • 自动转换 JSON 数据
  • 客户端支持防御 XSRF
1.3Aixos 浏览器支持
  • FireFox 65+
  • Chrome 72+
  • IE 8+
  • Edge
  • Safari 9+

二、第 2 章 Axios 方法的基本使用

2.1 vue 项目的创建
#安装vue脚手架
npm install -g @vue/cli

#通过脚手架创建一个vue项目
vue create axios-vue 回车
选择自定义安装 回车
选择Babel、Router、CSS Pre-processors、Linter / Formatter 回车
选择Use history mode for router? y 回车
选择Less 回车
选择ESLint with error prevention only 回车
选择Lint on save	回车
选择In dedicated config files 回车
选择Save this as a preset for future projects?    N 回车
...开始进行下载并且安装依赖

#启动axios-vue
cd axios-vue
npm run serve

#安装axios依赖
npm install axios -S
or
yarn add axios

#手动创建data.json(目录:public/data.json)
{
  "title": "axios vue",
  "createTime": "2019-9"
}

#在home.vue中通过axios请求data.json,并且查看打印结果(src/view/home.vue)
import axios from "axios";
created() {
    axios.get("/data.json").then(res => {
      console.log(res);
    });
}
2.2axios 请求方法及别名(get、post、put、patch、delete 方法)
  1. 2-2.vue
<template>
  <div class="home"></div>
</template>

<script>
/*
  axios请求方法:get、post、put、patch、delete

  get:获取数据
  post:提交数据(表单提交/文件上传)
  put:更新数据 (将所有的数据推送到后端)
  patch:更新数据 (只将修改的数据推送到后端)
  delete:删除数据

  具体的提交方式是由后端来定义的
 */
import axios from "axios";

export default {
  name: "axios-2",
  created() {
    //http://localhost:8080/data.json?id=1&name=%E5%BC%A0%E4%B8%89
    //get请求第一种方式
    axios
      .get("/data.json", {
        params: {
          id: 1,
          name: "张三"
        }
      })
      .then(res => {
        console.log(res);
      });
    //get请求第二种方式
    axios({
      method: "get",
      url: "/data.json",
      timeout: 1000,
      params: {
        id: 1,
        name: "张三"
      }
    }).then(res => {
      console.log(res);
    });

    //post 存在两种数据格式的提交
    //form-data 表单提交(图片上传、文件上传)
    //application/json

    //post请求第一种方式
    let data = {
      id: 1
    };
    axios.post("/post", data).then(() => {});
    //post请求第二种方式
    axios({
      method: "post",
      url: "/post",
      data: data
    }).then(() => {});

    //form-data的post请求方式
    let formData = new FormData();
    for (let key in data) {
      formData.append(key, data[key]);
    }
    axios.post("/post", formData).then(() => {});

    //那么put、patch也有两种方式类似于get、post,下面的演示咱们就只采取一种方式
    axios.put("/put", data).then(() => {});
    axios.patch("/patch", data).then(() => {});

    //delete请求第一种方式
    axios
      .delete("/delete", {
        params: {
          id: 1
        }
      })
      .then(() => {});
    axios
      .delete("/delete", {
        data: {
          id: 1
        }
      })
      .then(() => {});
    //delete请求第二种方式
    axios({
      method: "delete",
      url: "/delete",
      // params:{id:1},
      data: {
        id: 1
      }
    });
  }
};
</script>

2.在 router.js 当中增加 2-2.vue 的路由

{
      path: '/axios-2',
      name: 'axios-2',
      component: () => import(/* webpackChunkName: "axios-2" */ './views/2-2.vue')
}

3.创建一个 data.json (public/data.json)

{
  "title": "axios vue",
  "createTime": "2019-9"
}

4.那么请求地址就是:http://localhost:8080/axios-2

2.3 并发请求

1.2-3.vue 页面

<template>
  <div class="home">
    <img alt="Vue logo" src="../assets/logo.png" />
  </div>
</template>

<script>
/*
  并发请求: 同时进行多个请求,并统一处理返回值
*/
import axios from "axios";

export default {
  name: "axios-2",
  created() {
    //axios.all、axios.spread
    axios.all([axios.get("/data.json"), axios.get("/city.json")]).then(
      axios.spread((dataRes, cityRes) => {
        console.log("dataRes", dataRes);
        console.log("cityRes", cityRes);
      })
    );
  }
};
</script>

2.在 router.js 当中增加 2-3.vue 的路由

{
      path: '/axios-3',
      name: 'axios-3',
      component: () => import(/* webpackChunkName: "axios-3" */ './views/2-3.vue')
},

3.创建一个 data.json (public/city.json)

{
  "name": "北京"
}

4.那么请求地址就是:http://localhost:8080/axios-3

三、第 3 章 Axios 方法深入

3.1 创建 axios 实例

1.3-1.vue 页面

<template>
  <div class="home"></div>
</template>

<script>
/*
  axios的实例
  为什么我们要使用axios的实例呢?
  因为在开发的过程中,我们可能会有不同的baseURL、timeout
*/
import axios from "axios";

export default {
  name: "axios3-1",
  created() {
    //axios的实例
    let axios1 = axios.create({
      baseURL: "http://localhost:8080",
      timeout: 1000
    });
    let axios2 = axios.create({
      baseURL: "https://easy-mock.com/mock/5d5fb2ce9b58ad2249ff6c2b/mockapi",
      timeout: 5000
    });

    axios1.get("/data.json").then(res => {
      console.log(res);
    });
    axios2.get("/table/high/list").then(res => {
      console.log(res);
    });
  }
};
</script>

2.在 router.js 当中增加 3-1.vue 的路由

{
      path: '/axios3-1',
      name: 'axios3-1',
      component: () => import(/* webpackChunkName: "axios3-1" */ './views/3-1.vue')
}

3.那么请求地址就是:http://localhost:8080/axios3-1

3.2 axios 基本的配置参数,以及在开发中的应用

1.3-2.vue 页面

<template>
  <div class="home"></div>
</template>

<script>
import axios from "axios";

export default {
  name: "axios3-2",
  created() {
    //第一部分:axios基本的配置参数有哪些
    axios.create({
      baseURL: "http://localhost:8080", //请求域名,基本地址
      timeout: 1000, //请求超时时长(ms)
      method: "get", //请求方式(post、put、patch、delete)
      url: "/data.json", //请求路径(接口)
      headers: {
        token: ""
      }, //请求头
      params: {}, //请求参数拼接在url中
      data: {} //请求参数放在请求体里面
    });
    
    //第二部分:axios三种参数配置方式以及优先级
    //1.aixos的全局配置(优先级:低)
    axios.defaults.timeout = 1000;
    axios.defaults.baseURL = "http://localhost:8080";
    //2.axios的实例配置(优先级:中)
    let instance = axios.create();
    instance.defaults.timeout = 3000;
    //3.axios的请求配置(优先级:高)
    instance.get("/data.json", {
      timeout: 5000
    });

    //第三部分:axios实例在实际开发中的应用
    //创建axios1实例
    let axios1 = axios.create({
      baseURL: "http://localhost:8080",
      timeout: 1000
    });
    //创建axios2实例
    let axios2 = axios.create({
      baseURL: "https://easy-mock.com/mock/5d5fb2ce9b58ad2249ff6c2b/mockapi",
      timeout: 3000
    });

    //在axios1实例中设计的参数有:baseURL、timeout:1000、method、params
    axios1
      .get("/data.json", {
        params: {
          id: 1
        }
      })
      .then(res => {
        console.log(res);
      });
    //在axios1实例中设计的参数有:baseURL、timeout:5000、method、params
    axios2
      .get("/table/high/list", {
        timeout: 5000,
        params: {
          id: 1
        }
      })
      .then(res => {
        console.log(res);
      });
  }
};
</script>

2.在 router.js 当中增加 3-2.vue 的路由

{
      path: '/axios3-2',
      name: 'axios3-2',
      component: () => import(/* webpackChunkName: "axios3-2" */ './views/3-2.vue')
}

3.那么请求地址就是:http://localhost:8080/axios3-2

3.3 axios 拦截器

1.3-3.vue 页面

<template>
  <div class="home"></div>
</template>

<script>
/*
  拦截器:在请求或者响应被处理前拦截他们

  let instance = axios.create()
  请求拦截器 instance.interceptors.request.use(config=>return config,err=>return Promise.reject(err))
  响应拦截器 instance.interceptors.re.use(config=>return config,err=>return Promise.reject(err))
  取消拦截器 
*/
import axios from "axios";
import { Promise } from "q";
export default {
  name: "axios3-3",
  created() {
    let instance = axios.create({});
    //请求拦截器
    instance.interceptors.request.use(
      config => config,
      err => Promise.reject(err)
    );
    //响应拦截器
    instance.interceptors.response.use(res => res, err => Promise.reject(err));

    //取消拦截器(了解)
    let interceptors = axios.interceptors.request.use(config => {
      config.headers = {
        auth: true
      };
      return config;
    });
    axios.interceptors.request.eject(interceptors);

    //例子 登录状态(token:'') 不需要登录的接口
    let instance1 = axios.create({});
    instance1.interceptors.request.use(
      config => {
        config.headers.token = "";
        return config;
      },
      err => {
        return Promise.reject(err);
      }
    );
    //需要登录的接口
    let instance2 = axios.create({});
    instance2.get("/data.json");

    //移动端开发的接口
    let instance3 = axios.create({});
    instance3.interceptors.request.use(config => {
      $("#modal").show(); //loading 显示
      return config;
    });
    instance3.interceptors.response.use(
      res => {
        $("#modal").hide(); //loading 隐藏
        return res;
      },
      err => {
        $("#modal").hide(); //loading 隐藏
        return Promise.reject(err);
      }
    );
  }
};
</script>

2.在 router.js 当中增加 3-3.vue 的路由

{
      path: '/axios3-3',
      name: 'axios3-3',
      component: () => import(/* webpackChunkName: "axios3-3" */ './views/3-3.vue')
}

3.那么请求地址就是:http://localhost:8080/axios3-3

3.4 axios 错误处理

1.3-4.vue

<template>
  <div class="home"></div>
</template>

<script>
/*
  错误处理: 请求错误时进行的处理
*/
import axios from "axios";
import { Promise } from "q";
import { Toast } from "vant";
export default {
  name: "axios3-4",
  created() {
    axios.interceptors.request.use(
      config => {
        return config;
      },
      err => {
        return Promise.reject(err);
      }
    );
    axios.interceptors.response.use(
      res => {
        return res;
      },
      err => {
        return Promise.reject(err);
      }
    );
    axios
      .get("/data.json")
      .then(res => {
        console.log(res);
      })
      .catch(err => {
        console.log("err", err);
      });

    //例子: 在实际开发过程中,一般添加统一的错误处理
    let intance = axios.create({});
    intance.interceptors.request.use(
      config => {
        return config;
      },
      err => {
        //请求错误 一般http状态码以4开头,常见: 401超时,404 not found
        Toast.loading({
          mask: false,
          message: "加载中..."
        });
        return Promise.reject(err);
      }
    );
    intance.interceptors.response.use(
      res => {
        return res;
      },
      err => {
        //请求错误 一般http状态码以5开头,常见: 500系统错误,502系统重启
        Toast.loading({
          mask: false,
          message: "加载中..."
        });
        return Promise.reject(err);
      }
    );
    intance
      .get("/data1.json")
      .then(res => {
        console.log(res);
      })
      .catch(err => {
        console.log(err);
      });
  }
};
</script>

2.在 router.js 当中增加 3-4.vue 的路由

{
      path: '/axios3-4',
      name: 'axios3-4',
      component: () => import(/* webpackChunkName: "axios3-4" */ './views/3-4.vue')
}

3.安装 vue 的一个 UI 组件库:vant 、babel-plugin-import

npm install vant -S
npm install babel-plugin-import -D

4.配置 babel.config.js

module.exports = {
  presets: [
    '@vue/app'
  ],
  plugins: [
    ['import', {
      libraryName: 'vant',
      libraryDirectory: 'es',
      style: true
    }, 'vant']
  ]
}

5.那么请求地址就是:http://localhost:8080/axios3-4

3.5 axios 取消请求操作

1.3-5.vue 页面

<template>
  <div class="home"></div>
</template>

<script>
/*
  取消请求: 用于取消正在进行的http请求(了解)
*/
import axios from "axios";

export default {
  name: "axios3-5",
  created() {
    let source = axios.CancelToken.source();
    axios
      .get("/data.json", {
        cancelToken: source.token
      })
      .then(res => {
        console.log(res);
      })
      .catch(err => {
        console.log(err);
      });

    //取消请求操作(message 可选)
    source.cancel("cancel http");

    //什么情况下可能用到取消请求
    //在执行大量导出数据、或者查询数据的时候,请求时间过长3-5秒,这个过程我们可以进行取消请求的操作
  }
};
</script>

2.在 router.js 当中增加 3-5.vue 的路由

{
      path: '/axios3-5',
      name: 'axios3-5',
      component: () => import(/* webpackChunkName: "axios3-5" */ './views/3-5.vue')
}

3.那么请求地址就是:http://localhost:8080/axios3-5

四、第 4 章 Axios 实战

4.1 项目环境配置

1.安装 Vue 的一个 Ui 组件库:Vant(上面第 3 章咱们安装过了,这就不在重复赘述了)
2.启动一个 node 服务
下载 node 服务代码:https://github.com/web-gm/axios_node_api

cd axios_node_api
npm install
npm start

3.验证 node 服务是否请求成功:http://localhost:9000/api/contactList
看到数据则表示启动成功

4.2 项目实战联系人的(添加、编辑、删除)操作

1.新建 ContactList.vue 页面,引入需要的组件库,组件当中调用 UI 组件,模块中渲染组件,初始化组件数据,添加组件的事件等等操作

<template>
  <div class="home">
    <van-contact-list :list="list" @add="onAdd" @edit="onEdit" />

    <!-- 联系人编辑 -->
    <van-popup v-model="showEdit" position="bottom">
      <van-contact-edit
        :contact-info="editingContact"
        :is-edit="isEdit"
        @save="onSave"
        @delete="onDelete"
      />
    </van-popup>
  </div>
</template>

<script>
import axios from "axios";
import { Popup, ContactList, ContactEdit, Toast } from "vant";

export default {
  name: "cantactList",
  components: {
    [Popup.name]: Popup,
    [ContactList.name]: ContactList,
    [ContactEdit.name]: ContactEdit
  },
  data() {
    return {
      instance: null,
      editingContact: {},
      showEdit: false,
      isEdit: false,
      list: []
    };
  },
  created() {
    this.instance = axios.create({
      baseURL: "http://localhost:9000/api",
      timeout: 5000
    });
    this.getContactList();
  },
  methods: {
    //联系人列表
    getContactList() {
      this.instance
        .get("/contactList")
        .then(res => {
          console.log(res);
          if (res.data.code === 200) {
            this.list = res.data.data;
          }
        })
        .catch(err => {
          console.log(err);
          Toast("请求失败,请稍后重试");
        });
    },
    // 添加联系人
    onAdd() {
      this.showEdit = true;
      this.isEdit = false;
      this.editingContact = {};
    },

    // 编辑联系人
    onEdit(item) {
      this.isEdit = true;
      this.showEdit = true;
      this.editingContact = item;
    },

    // 保存联系人
    onSave(info) {
      if (this.isEdit) {
        //编辑操作
        this.instance
          .put("/contact/edit", info)
          .then(res => {
            if (res.data.code === 200) {
              Toast("编辑成功");
              this.getContactList();
            }
          })
          .catch(() => {
            Toast("请求失败,请稍后重试");
          });
      } else {
        //新建操作
        this.instance
          .post("/contact/new/json", info)
          .then(res => {
            if (res.data.code === 200) {
              Toast("新建成功");
              this.getContactList();
            }
          })
          .catch(() => {
            Toast("请求失败,请稍后重试");
          });
      }
      this.showEdit = false;
    },

    // 删除联系人
    onDelete(info) {
      this.instance
        .delete("/contact", {
          params: {
            id: info.id
          }
        })
        .then(() => {
          Toast("删除成功");
          this.getContactList();
        })
        .catch(() => {
          Toast("请求失败,请稍后重试");
        });
      this.showEdit = false;
    }
  }
};
</script>
<style scoped>
.van-contact-list__add {
  z-index: 0;
}
.van-popup {
  height: 100%;
}
</style>

2.在 router.js 当中增加 ContactList.vue 的路由

{
      path: '/contactList',
      name: 'contactList',
      component: () => import(/* webpackChunkName: "contactList" */ './views/ContactList.vue')
}

3.那么请求地址就是:http://localhost:8080/contactList

4.3 axios 的封装

1.为什么要对 axios 进行封装呢?

  • 进行统一的容错处理
  • 进行公共入参的处理比如 headers
  • 对 api 接口地址进行统一管理
  • 如果接口请求时间过长,可以进行统一的正在加载中的处理

2.axios 封装分为哪几步骤呢?

1.API 接口地址的抽离
在 src 目录下创建一个 service/contactApi.js 的文件

const CONTACT_API = {
  //获取联系人列表
  getContactList: {
    method: 'get',
    url: '/contactList'
  },
  //新建联系人 form-data
  newContactForm: {
    method: 'post',
    url: '/contact/new/form'
  },
  //新建联系人 application/json
  newContactJson: {
    method: 'post',
    url: '/contact/new/json'
  },
  //编辑联系人
  editContact: {
    method: 'put',
    url: '/contact/edit'
  },
  //删除联系人
  delContact: {
    method: 'delete',
    url: '/contact'
  }
}

export default CONTACT_API;

2.对 axios 的封装
在 src 目录下创建一个 service/http.js 的文件

import axios from 'axios';
import service from './contactApi';
import { Toast } from 'vant';
Toast.allowMultiple();//多了Toast并存

//service 循环遍历出不同的请求方法

let instance = axios.create({
  baseURL: 'http://localhost:9000/api',
  timeout: 5000
})

const Http = {}; //包裹请求方法的容器
let globalLoadingStatus; //默认情况请求接口会出现”正在加载...“,如果入参idLoading:false,则就不会出现”正在加载“
//请求格式/参数的统一
for (let key in service) {
  let api = service[key]; //method,url

  //async 避免进入回调地狱
  Http[key] = async function (
    params,//请求参数 get、delete(url)、post、put、patch(data)
    isFormData = false,//是否是form-data的请求
    config = {}, //配置参数
    idLoading = true,//默认正在加载显示
  ) {
    globalLoadingStatus = idLoading;
    let newParams = {};
    //判断 content-type 是否是form-data
    if (params && isFormData) {
      newParams = new FormData();
      for (let i in params) {
        newParams.append(i, params[i])
      }
    } else {
      newParams = params
    }

    //不同请求的判断
    let response = {};
    if (api.method === 'post' || api.method === 'put' || api.method === 'patch') {
      try {
        response = await instance[api.method](api.url, newParams, config)
      } catch (err) {
        response = err
      }
    } else if (api.method === 'get' || api.method === 'delete') {
      config.params = newParams;
      try {
        response = await instance[api.method](api.url, config)
      } catch (err) {
        response = err
      }
    }
    return response
  }
}

//请求 拦截器
let toastLoading;
instance.interceptors.request.use(config => {
  globalLoadingStatus = config.params ? (config.params.isLoading ? true : false) : globalLoadingStatus
  //发起请求前
  if (globalLoadingStatus) {
    toastLoading = Toast.loading({
      mask: false,//遮罩层
      duration: 0,//一直存在
      forbidClick: true,//禁止点击
      message: '加载中...'
    })
  }
  return config;
}, err => {
  //请求错误
  toastLoading && toastLoading.clear();
  return Promise.reject(err)
})

//响应 拦截器
instance.interceptors.response.use(res => {
  //请求成功了
  toastLoading && toastLoading.clear();
  if (res.data && res.status === 200) {
    return res.data
  }
}, err => {
  //
  Toast('请求错误,请稍后重试');
  toastLoading && toastLoading.clear();
  return Promise.reject(err)
})

export default Http;

3.新增页面 ContactList2.vue 页面

<template>
  <div class="home">
    <van-contact-list :list="list" @add="onAdd" @edit="onEdit" />

    <!-- 联系人编辑 -->
    <van-popup v-model="showEdit" position="bottom">
      <van-contact-edit
        :contact-info="editingContact"
        :is-edit="isEdit"
        @save="onSave"
        @delete="onDelete"
      />
    </van-popup>
  </div>
</template>

<script>
import { Popup, ContactList, ContactEdit, Toast } from "vant";

export default {
  name: "cantactList",
  components: {
    [Popup.name]: Popup,
    [ContactList.name]: ContactList,
    [ContactEdit.name]: ContactEdit
  },
  data() {
    return {
      instance: null,
      editingContact: {},
      showEdit: false,
      isEdit: false,
      list: []
    };
  },
  created() {
    this.getContactList();
  },
  methods: {
    //联系人列表
    async getContactList() {
      let res = await this.$Http.contactList();
      this.list = res.data;
    },
    // 添加联系人
    onAdd() {
      this.showEdit = true;
      this.isEdit = false;
      this.editingContact = {};
    },

    // 编辑联系人
    onEdit(item) {
      this.isEdit = true;
      this.showEdit = true;
      this.editingContact = item;
    },

    // 保存联系人
    async onSave(info) {
      if (this.isEdit) {
        //编辑操作
        let res = await this.$Http.editContact(info);
        if (res.code === 200) {
          Toast("编辑成功");
          this.getContactList();
        }
      } else {
        //新建操作
        let res = await this.$Http.newContactJson(info);
        if (res.code === 200) {
          Toast("新建成功");
          this.getContactList();
        }
      }
      this.showEdit = false;
    },

    // 删除联系人
    async onDelete(info) {
      let res = await this.$Http.delContact({ id: info.id });
      if (res.code === 200) {
        Toast("删除成功");
        this.getContactList();
      }
      this.showEdit = false;
    }
  }
};
</script>
<style scoped>
.van-contact-list__add {
  z-index: 0;
}
.van-popup {
  height: 100%;
}
</style>

4.在 router.js 当中增加 ContactList2.vue 的路由

{
      path: '/contactList2',
      name: 'contactList2',
      component: () => import(/* webpackChunkName: "contactList2" */ './views/ContactList2.vue')
    },

5.那么请求地址就是:http://localhost:8080/contactList2

五、第 5 章 Axios 总结

5.1 总结

针对 axios 封装要结合自己项目来进行封装,从而进行调整,这个案例只给了封装的一种思路,仅供在今后开发 axios 封装的参考。
1.axios 封装的一些扩展

  • 可以给请求添加统一的 loading (案例中已经给了一种方案)
  • 在 headers 添加 token 鉴权 (针对一些有需要登录接口或者后端需要传的一些 header 值)
  • 添加统一的错误处理,验证失败后一些页面跳转、清除一些验证数据等等

案例代码仓库地址: https://github.com/fx35792/axios-vue

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...