nuxt.js中使用axios以及二次封装

nuxt.js中使用axios以及二次封装

一、背景与问题

在基于Vue.js的nuxt.js项目中,前后端分离架构下的数据交互是核心需求。axios作为主流的HTTP客户端库,其使用场景包括:

  1. 页面组件中发起的API请求
  2. API模块中暴露的接口
  3. 跨域请求的处理
  4. 需要统一处理认证、错误、日志等场景

但直接使用axios存在以下痛点:

  • 重复代码:每个请求都需要处理headers、错误处理等
  • 统一性差:不同页面组件的请求格式不一致
  • 安全隐患:未统一处理token、跨域等安全机制
  • 性能问题:未优化重复请求、未处理缓存等

二、基本原理

nuxt.js基于Vue.js,其核心架构包含:

  • pages/:页面组件
  • api/:API接口
  • plugins/:插件系统
  • components/:通用组件
  • layouts/:布局模板

axios在nuxt中的工作原理:

  1. nuxt.config.js中配置axios
  2. plugins/目录创建axios插件,注册全局实例
  3. 使用拦截器统一处理请求和响应
  4. 在页面组件中通过this.$axios调用

三、环境准备

# 创建nuxt项目
npx create-nuxt-app my-app
cd my-app
npm install axios

四、核心实现

1. 基础封装(无拦截器)

// plugins/axios.js
import axios from 'axios'

export default (ctx, inject) => {
  const api = axios.create({
    baseURL: process.env.API_URL || 'https://api.example.com'
  })
  
  inject('api', api)
}

关键点:

  • 使用axios.create创建实例
  • 通过inject注册为全局可用
  • 需在nuxt.config.js中注册插件

2. 带拦截器的封装

// plugins/axios.js
import axios from 'axios'

export default (ctx, inject) => {
  const api = axios.create({
    baseURL: process.env.API_URL || 'https://api.example.com',
    timeout: 10000
  })

  // 请求拦截器
  api.interceptors.request.use(config => {
    const token = ctx.$auth.getToken()
    if (token) {
      config.headers.Authorization = `Bearer ${token}`
    }
    return config
  }, error => {
    return Promise.reject(error)
  })

  // 响应拦截器
  api.interceptors.response.use(response => {
    if (response.data.code === 200) {
      return response.data.data
    } else {
      throw new Error(response.data.message)
    }
  }, error => {
    if (error.response?.status === 401) {
      ctx.$auth.logout()
    }
    return Promise.reject(error)
  })

  inject('api', api)
}

关键点:

  • 使用拦截器统一处理认证信息
  • 响应拦截器统一处理错误码
  • 支持401错误的自动登出
  • 可自定义错误处理逻辑

3. 带缓存的封装

// plugins/axios.js
import axios from 'axios'
import { useLocalStorage } from '@vueuse/core'

export default (ctx, inject) => {
  const api = axios.create({
    baseURL: process.env.API_URL || 'https://api.example.com',
    timeout: 10000
  })

  // 响应拦截器
  api.interceptors.response.use(response => {
    const { url } = response.config
    if (url && url.includes('/cache')) {
      const cacheKey = url.replace('/cache', '')
      const cache = useLocalStorage('cache', {})
      cache.value[cacheKey] = response.data
    }
    return response
  }, error => {
    return Promise.reject(error)
  })

  inject('api', api)
}

关键点:

  • 使用@vueuse/core实现本地缓存
  • 针对特定接口添加缓存逻辑
  • 可结合Cache-Control头实现服务端缓存

五、完整案例

1. 用户登录功能实现

页面组件(pages/login.vue)

<template>
  <div>
    <input v-model="username" placeholder="用户名" />
    <input v-model="password" type="password" placeholder="密码" />
    <button @click="login">登录</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      username: '',
      password: ''
    }
  },
  methods: {
    async login() {
      try {
        const data = await this.$api.post('/auth/login', {
          username: this.username,
          password: this.password
        })
        console.log('登录成功:', data)
        this.$auth.setUser(data.user)
      } catch (error) {
        console.error('登录失败:', error)
      }
    }
  }
}
</script>

API接口(api/auth.js)

export default {
  async login({ username, password }) {
    const response = await this.$api.post('/auth/login', {
      username,
      password
    })
    return response
  }
}

插件配置(nuxt.config.js)

export default {
  modules: [
    '@nuxtjs/axios'
  ],
  axios: {
    baseURL: process.env.API_URL || 'https://api.example.com'
  }
}

安全配置(plugins/auth.js)

export default (ctx, inject) => {
  const { $axios } = ctx
  const auth = {
    setUser(user) {
      ctx.$storage.set('user', user)
    },
    getToken() {
      const user = ctx.$storage.get('user')
      return user?.token
    },
    logout() {
      ctx.$storage.remove('user')
      ctx.$router.push('/login')
    }
  }
  inject('auth', auth)
}

关键点:

  • 使用@nuxtjs/axios模块
  • 统一的API调用方式
  • 响应式数据处理
  • 安全存储机制

六、源码解析

1. axios实例创建

const api = axios.create({
  baseURL: process.env.API_URL || 'https://api.example.com',
  timeout: 10000
})
  • baseURL设置统一的API基础地址
  • timeout设置请求超时时间
  • process.env.API_URL支持环境变量配置

2. 请求拦截器

api.interceptors.request.use(config => {
  const token = ctx.$auth.getToken()
  if (token) {
    config.headers.Authorization = `Bearer ${token}`
  }
  return config
}, error => {
  return Promise.reject(error)
})
  • 从auth模块获取token
  • 添加Authorization
  • 处理网络错误

3. 响应拦截器

api.interceptors.response.use(response => {
  if (response.data.code === 200) {
    return response.data.data
  } else {
    throw new Error(response.data.message)
  }
}, error => {
  if (error.response?.status === 401) {
    ctx.$auth.logout()
  }
  return Promise.reject(error)
})
  • 统一处理200响应
  • 自动处理401错误
  • 抛出错误继续处理

七、进阶使用

1. 搭建API网关

// plugins/api.js
export default (ctx, inject) => {
  const api = axios.create({
    baseURL: process.env.API_URL || 'https://api.example.com'
  })

  inject('api', {
    get: (url, params) => api.get(url, { params }),
    post: (url, data) => api.post(url, data),
    put: (url, data) => api.put(url, data),
    delete: (url) => api.delete(url)
  })
}

2. 响应格式标准化

api.interceptors.response.use(response => {
  const { data } = response
  if (data.code === 200) {
    return data.data
  } else {
    const error = new Error(data.message)
    error.code = data.code
    throw error
  }
}, error => {
  if (error.code === 401) {
    ctx.$auth.logout()
  }
  return Promise.reject(error)
})

3. 跨域支持

// nuxt.config.js
export default {
  modules: [
    '@nuxtjs/axios'
  ],
  axios: {
    baseURL: process.env.API_URL || 'https://api.example.com',
    headers: {
      common: {
        'Content-Type': 'application/json'
      }
    }
  }
}

八、性能与工程实践

1. 性能优化

1. 缓存策略

// 使用本地缓存
const cache = useLocalStorage('cache', {})

api.interceptors.response.use(response => {
  const { url } = response.config
  if (url && url.includes('/cache')) {
    const cacheKey = url.replace('/cache', '')
    cache.value[cacheKey] = response.data
  }
  return response
})

2. 资源预加载

// 在页面加载时预加载常用接口
mounted() {
  this.$api.get('/common/data').catch(err => {
    console.error('预加载失败:', err)
  })
}

3. 请求合并

// 使用request-promise库合并请求
const promises = [this.$api.get('/data1'), this.$api.get('/data2')]
Promise.all(promises).then(responses => {
  // 处理所有响应
})

2. 安全实践

1. HTTPS强制

// nuxt.config.js
export default {
  modules: [
    '@nuxtjs/axios'
  ],
  axios: {
    baseURL: process.env.API_URL || 'https://api.example.com',
    timeout: 10000,
    httpsAgent: {
      rejectUnauthorized: false
    }
  }
}

2. 安全头设置

// 在插件中添加安全头
api.defaults.headers.post['X-Requested-With'] = 'XMLHttpRequest'
api.defaults.headers.common['X-Content-Type-Options'] = 'nosniff'

3. 跨域策略

// 在服务器端配置CORS
const cors = require('cors')
app.use(cors({
  origin: ['https://your-app.com'],
  methods: ['GET', 'POST'],
  allowedHeaders: ['Content-Type', 'Authorization']
}))

九、常见问题与踩坑

1. 常见错误

错误1:跨域问题

# 控制台报错
Access to XMLHttpRequest at 'https://api.example.com/api' from origin 'http://localhost:3000' has been blocked by CORS policy

解决办法

  • 使用@nuxtjs/axios模块配置CORS
  • 配置服务器端CORS策略
  • 使用代理服务器(nuxt.config.js中配置proxy

错误2:请求未携带token

// 控制台报错
401: Unauthorized

解决办法

  • 确认token存储正确(使用localStorageVuex
  • 检查请求拦截器是否正确添加了Authorization头
  • nuxt.config.js中配置axiosheaders

错误3:响应格式不一致

// 控制台报错
TypeError: Cannot read property 'data' of undefined

解决办法

  • 统一响应格式(如返回{ code, data, message }
  • 在响应拦截器中统一处理数据
  • 在页面组件中使用try/catch捕获异常

2. 性能问题

问题1:频繁重复请求

// 错误代码
async function fetchData() {
  const data1 = await this.$api.get('/data1')
  const data2 = await this.$api.get('/data2')
  // 重复请求
}

优化方案

  • 使用request-promise合并请求
  • 添加请求缓存机制
  • 使用axioscache插件

问题2:未处理超时请求

// 错误代码
async function fetchData() {
  const data = await this.$api.get('/data')
}

优化方案

  • 设置timeout参数
  • 添加超时处理逻辑
  • 使用axiosCancelToken取消请求

十、最佳实践

1. 接口封装规范

  • 统一的接口格式:{ code, data, message }
  • 接口分类:/api/下按模块划分
  • 接口版本控制:/api/v1/
  • 接口文档:使用Swagger生成API文档

2. 安全实践

  • 强制HTTPS
  • 使用JWT进行认证
  • 设置CORS策略
  • 使用CSRF防护
  • 对敏感接口进行速率限制

3. 性能优化

  • 使用缓存策略(本地/服务端)
  • 合并重复请求
  • 使用请求节流
  • 使用预加载策略
  • 使用懒加载策略

4. 异常处理

  • 统一的错误处理逻辑
  • 错误日志记录
  • 错误分类处理(网络错误、业务错误)
  • 错误重试机制

十一、总结

在nuxt.js中使用axios及其二次封装,需要考虑以下核心要素:

  1. 统一性:通过拦截器实现请求和响应的统一处理
  2. 安全性:正确处理认证、授权、CORS等安全机制
  3. 可维护性:良好的封装结构便于后续维护
  4. 性能优化:通过缓存、合并请求等手段提升性能
  5. 错误处理:完善的错误处理机制提升健壮性

在实际项目中,建议:

  • 对所有API接口进行统一封装
  • 使用拦截器处理认证和错误
  • 根据业务需求选择合适的缓存策略
  • 对关键接口进行性能优化
  • 保持良好的代码结构和文档

需要注意的是,这种封装方案适合中大型项目,对于小型项目或简单功能,直接使用axios可能更简单直接。同时,在涉及复杂业务逻辑时,需要根据具体情况调整封装策略。

评论已关闭

推荐阅读

AIGC实战——Transformer模型
2024年12月01日
Socket TCP 和 UDP 编程基础(Python)
2024年11月30日
python , tcp , udp
如何使用 ChatGPT 进行学术润色?你需要这些指令
2024年12月01日
AI
最新 Python 调用 OpenAi 详细教程实现问答、图像合成、图像理解、语音合成、语音识别(详细教程)
2024年11月24日
ChatGPT 和 DALL·E 2 配合生成故事绘本
2024年12月01日
omegaconf,一个超强的 Python 库!
2024年11月24日
【视觉AIGC识别】误差特征、人脸伪造检测、其他类型假图检测
2024年12月01日
[超级详细]如何在深度学习训练模型过程中使用 GPU 加速
2024年11月29日
Python 物理引擎pymunk最完整教程
2024年11月27日
MediaPipe 人体姿态与手指关键点检测教程
2024年11月27日
深入了解 Taipy:Python 打造 Web 应用的全面教程
2024年11月26日
基于Transformer的时间序列预测模型
2024年11月25日
Python在金融大数据分析中的AI应用(股价分析、量化交易)实战
2024年11月25日
AIGC Gradio系列学习教程之Components
2024年12月01日
Python3 `asyncio` — 异步 I/O,事件循环和并发工具
2024年11月30日
llama-factory SFT系列教程:大模型在自定义数据集 LoRA 训练与部署
2024年12月01日
Python 多线程和多进程用法
2024年11月24日
Python socket详解,全网最全教程
2024年11月27日
python之plot()和subplot()画图
2024年11月26日
理解 DALL·E 2、Stable Diffusion 和 Midjourney 工作原理
2024年12月01日