Vue基础教程(185)axios创建实例:Axios不发疯指南:告别复制粘贴,用“创造”代替“苦力”!

深度分析Vue基础教程之Axios创建实例

兄弟们,姐妹们,摸鱼划水的各位程序员们,今天咱们来聊一个在Vue项目中几乎必用,但很多人却“用而不知其所以然”的神器——Axios实例创建

你是不是经常在代码里看到这样的场景?

// 在每个.vue文件里,都是这样的“老三样”
axios.get('https://api.myawesomeapp.com/v1/users?page=1&limit=10', {
  headers: {
    'Authorization': 'Bearer your_token_here'
  }
})
.then(...)

axios.post('https://api.myawesomeapp.com/v1/posts', data, {
  headers: {
    'Authorization': 'Bearer your_token_here'
  }
})
.then(...)

发现痛点了吗?第一,那个又臭又长的基地址 https://api.myawesomeapp.com/v1 像牛皮癣一样粘得到处都是,万一后端大哥哪天心情好想换个域名,你就等着全局搜索替换到天亮吧。第二,那个Authorization头,每个请求都要手动塞进去,不优雅,且容易漏。

这感觉就像,你想吃个泡面,却每次都要从种小麦开始。太原始了!太辛苦了!是时候进化了!

第一部分:为啥要创建实例?—— 从“打零工”到“正式工”的飞跃

你可以把默认的 axios 对象理解成一个“打零工”的。啥活都干,但身上没啥固定的标签。你今天让他去A公司(apiA.com)取个文件,明天让他去B公司(apiB.com)送个快递,每次都得把完整的地址和通行证(headers)告诉他。

创建Axios实例,就相当于你为公司里的某个特定部门(比如用户管理部),雇佣了一个“正式工”

这个“正式工”天生就自带一些属性和技能:

  • 他的工位是固定的:基地址(baseURL)已经写进了他的劳动合同。
  • 他拥有公司门禁卡:通用的请求头(headers)在他入职时就配发了。
  • 他甚至还懂点“读心术”:请求和响应拦截器,就像他的职业习惯,能在办事前后自动完成一些操作。

这样一来,当你需要这个部门办事时,你只需要对这个“正式工”说:“嘿,去给我把用户列表拿过来(/users)。” 他就能自动拼好完整的地址,带上门禁卡,完美地完成任务。

这么做的好处,用脚趾头想想都知道:

  1. DRY原则(Don‘t Repeat Yourself):告别重复代码,告别“复制粘贴”地狱。
  2. 维护性飙升:配置在一处修改,全局生效。后端换地址?改一下实例的baseURL就行,稳如老狗。
  3. 职责清晰:可以为不同的后端API(比如主API、文件上传API)创建不同的实例,井水不犯河水。
  4. 便于封装:拦截器等高级功能有了统一的“挂载点”,代码结构更清晰。
第二部分:手把手教你“创造”一个Axios实例

理论吹得再响,不如代码来得实在。上才艺!

1. 安装与引入(准备工作)

首先,确保你的项目里已经安装了axios。

npm install axios
# 或
yarn add axios

然后,我们通常不会在组件里直接创建实例,而是单独建立一个文件来“工厂化生产”我们的axios“正式工”。比如 src/utils/request.js

2. 基础创建:打造你的第一个“专属员工”

// src/utils/request.js
import axios from 'axios';

// 开始“创造”一个Axios实例,给它起名叫 `apiClient`
const apiClient = axios.create({
  // 它的固定工位:我们的后端API基地址
  baseURL: 'https://api.myawesomeapp.com/v1',
  // 它的超时耐心,超过5秒没回应就撂挑子
  timeout: 5000,
  // 它自带的身份标识(请求头)
  headers: {
    'Content-Type': 'application/json',
    // 这里可以先放一些通用的头,比如API密钥(非敏感)
    // 'X-API-KEY': 'your-general-api-key'
  }
});

// 把这个打造好的“员工”导出,供其他部门(组件)使用
export default apiClient;

现在,在Vue组件里,我们就可以愉快地使用了:

<script>
// 1. 引入我们定制好的“员工”,而不是那个“零工”axios
import apiClient from '@/utils/request';

export default {
  data() {
    return {
      users: []
    };
  },
  async created() {
    try {
      // 2. 使用它!只需要关心具体的接口路径
      const response = await apiClient.get('/users');
      this.users = response.data;
    } catch (error) {
      console.error('获取用户列表失败:', error);
    }
  }
};
</script>

看看,代码是不是瞬间清爽了?我们再也不需要写完整的URL了!

第三部分:注入灵魂——拦截器黑科技

如果说baseURL是给了实例一个身体,那**拦截器(Interceptors)**就是它的灵魂。它能让你在请求发出前和响应返回后,为所欲为(不是)。

1. 请求拦截器:像个贴心小秘书

想象一下,每次你的“员工”出门办事前,都自动检查一下有没有带令牌(token)。

// 在 src/utils/request.js 里,接着上面的代码写

// 请求拦截器:在请求发出之前做些事情
apiClient.interceptors.request.use(
  (config) => {
    // config 是本次请求的配置对象,我们可以修改它
    // 假设我们把用户token存在了Vuex或者localStorage
    const token = localStorage.getItem('user-token');
    if (token) {
      // 如果存在token,就自动添加到请求头里
      config.headers.Authorization = `Bearer ${token}`;
    }
    console.log('请求即将发出:', config);
    return config; // 必须返回config
  },
  (error) => {
    // 对请求错误做些什么(比如配置错误,网络断开等)
    return Promise.reject(error);
  }
);

这样,我们就不用在每个请求里手动写 headers: { ‘Authorization’: ... } 了。拦截器自动帮我们搞定!

2. 响应拦截器:像个严格的质量检查员

当“员工”办完事回来,我们可以先让他把结果给“质检员”看看。

// 响应拦截器:在响应返回之后,then/catch之前做些事情
apiClient.interceptors.response.use(
  (response) => {
    // 2xx 范围内的状态码都会触发该函数。
    // 对响应数据直接进行处理,比如我们只关心data里的数据
    console.log('请求成功响应:', response);
    return response.data; // 这里直接返回了response.data,后续then里拿到就是这个!
  },
  (error) => {
    // 超出 2xx 范围的状态码都会触发该函数。
    console.error('请求出错:', error.response);

    // 统一错误处理
    if (error.response.status === 401) {
      // 如果是401未授权,可能是token过期,跳转到登录页
      alert('登录已过期,请重新登录!');
      router.push('/login');
    } else if (error.response.status >= 500) {
      // 服务器错误
      alert('服务器开小差了,请稍后再试!');
    }

    // 将错误继续抛给具体的请求处理逻辑
    return Promise.reject(error);
  }
);

加了响应拦截器后,组件里的代码可以进一步简化:

<script>
import apiClient from '@/utils/request';

export default {
  async created() {
    try {
      // 因为响应拦截器直接返回了 response.data,所以这里拿到就是数据本身!
      const userData = await apiClient.get('/users');
      this.users = userData; // 直接赋值,无需再 .data
    } catch (error) {
      // 通用的错误(如401)已被拦截器处理,这里可以处理特定于这个请求的错误
      console.error('这个请求的特殊错误处理:', error);
    }
  }
};
</script>
第四部分:完整示例——打造一个企业级Vue项目请求中心

让我们把所有代码整合到一起,形成一个生产环境可用的、功能完善的请求模块。

src/utils/request.js

import axios from 'axios';
import { MessageBox, Message } from 'element-ui'; // 假设使用Element UI的提示组件
import router from '@/router'; // 你的路由实例

// 创建实例
const service = axios.create({
  baseURL: process.env.VUE_APP_BASE_API, // 从环境变量读取,非常灵活!
  timeout: 10000, // 10秒超时
});

// 请求拦截器
service.interceptors.request.use(
  (config) => {
    // 在发送请求之前做些什么
    const token = localStorage.getItem('access-token');
    if (token) {
      config.headers['Authorization'] = `Bearer ${token}`;
    }
    // 可以根据需要添加其他逻辑,比如显示全局Loading...
    // showFullScreenLoading();
    return config;
  },
  (error) => {
    // 对请求错误做些什么
    // hideFullScreenLoading();
    console.error('Request Error:', error);
    return Promise.reject(error);
  }
);

// 响应拦截器
service.interceptors.response.use(
  (response) => {
    // hideFullScreenLoading();
    const res = response.data;

    // 假设我们的后端统一返回格式为 { code: 200, data: ..., message: 'success' }
    if (res.code === 200) {
      return res.data; // 返回我们真正需要的数据
    } else {
      // 业务逻辑错误(比如code=50001代表密码错误)
      // 统一提示错误信息
      Message({
        message: res.message || 'Error',
        type: 'error',
        duration: 5 * 1000,
      });

      // 可以针对特定的code做特殊操作,比如token过期
      if (res.code === 50008 || res.code === 50012) {
        // 触发登出逻辑,清除token,跳转登录页
        MessageBox.confirm('登录状态已过期,请重新登录', '确认登出', {
          confirmButtonText: '重新登录',
          cancelButtonText: '取消',
          type: 'warning'
        }).then(() => {
          localStorage.removeItem('access-token');
          router.push('/login');
        });
      }
      return Promise.reject(new Error(res.message || 'Error'));
    }
  },
  (error) => {
    // hideFullScreenLoading();
    console.error('Response Error:', error);

    let { message } = error;
    if (error.response) {
      // 有响应但状态码不是2xx
      switch (error.response.status) {
        case 400:
          message = '请求错误';
          break;
        case 401:
          message = '未授权,请登录';
          // 跳转登录页
          setTimeout(() => {
            router.push('/login');
          }, 1000);
          break;
        case 403:
          message = '拒绝访问';
          break;
        case 404:
          message = `请求地址出错: ${error.response.config.url}`;
          break;
        case 500:
          message = '服务器内部错误';
          break;
        case 502:
          message = '网关错误';
          break;
        default:
          message = '网络连接错误';
      }
    } else if (error.request) {
      // 请求发出去了但没有收到响应
      message = '网络连接异常,请检查您的网络!';
    } else {
      // 在设置请求时触发了一些错误
      message = error.message;
    }

    Message({
      message,
      type: 'error',
      duration: 5 * 1000,
    });
    return Promise.reject(error);
  }
);

export default service;

在组件中使用 (SomeComponent.vue)

<template>
  <div>
    <ul>
      <li v-for="user in userList" :key="user.id">{{ user.name }}</li>
    </ul>
  </div>
</template>
<script>
import apiClient from '@/utils/request'; // 导入我们封装的实例

export default {
  name: 'UserList',
  data() {
    return {
      userList: []
    };
  },
  async created() {
    await this.fetchUsers();
  },
  methods: {
    async fetchUsers() {
      try {
        // 简洁,清晰,专注业务逻辑!
        this.userList = await apiClient.get('/users');
      } catch (error) {
        // 错误已经被拦截器统一处理,这里通常无需再写通用错误提示
        // 但如果这个页面有特殊的错误处理逻辑,可以在这里写
        console.error('获取用户列表失败,需要特殊处理:', error);
      }
    }
  }
};
</script>
结语:从“会用”到“用好”

看到这里,你是不是已经彻底明白,为啥创建Axios实例是Vue项目开发的“必修课”了?它不仅仅是一个API,更是一种工程化思想和代码组织能力的体现。

从此以后,请告别那种“打零工”式的原始axios用法。拥抱“创造”,打造属于你自己的、功能强大且优雅的HTTP客户端。你的代码会感谢你,你的队友也会感谢你!

好了,今天的“不发疯指南”就到这里。快去给你的项目也“雇佣”一个得力的Axios“正式工”吧!

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

值引力

持续创作,多谢支持!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值