'# nuxt.js中使用axios以及二次封装
一、背景与问题
在基于Vue.js的nuxt.js项目中,前后端分离架构下的数据交互是核心需求。axios作为主流的HTTP客户端库,其使用场景包括:
- 页面组件中发起的API请求
- API模块中暴露的接口
- 跨域请求的处理
- 需要统一处理认证、错误、日志等场景
但直接使用axios存在以下痛点:
- 重复代码:每个请求都需要处理headers、错误处理等
- 统一性差:不同页面组件的请求格式不一致
- 安全隐患:未统一处理token、跨域等安全机制
- 性能问题:未优化重复请求、未处理缓存等
二、基本原理
nuxt.js基于Vue.js,其核心架构包含:
pages/:页面组件api/:API接口plugins/:插件系统components/:通用组件layouts/:布局模板
axios在nuxt中的工作原理:
- 在
nuxt.config.js中配置axios - 在
plugins/目录创建axios插件,注册全局实例 - 使用拦截器统一处理请求和响应
- 在页面组件中通过
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存储正确(使用
localStorage或Vuex) - 检查请求拦截器是否正确添加了Authorization头
- 在
nuxt.config.js中配置axios的headers
错误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合并请求 - 添加请求缓存机制
- 使用
axios的cache插件
问题2:未处理超时请求
// 错误代码
async function fetchData() {
const data = await this.$api.get('/data')
}
优化方案:
- 设置
timeout参数 - 添加超时处理逻辑
- 使用
axios的CancelToken取消请求
十、最佳实践
1. 接口封装规范
- 统一的接口格式:
{ code, data, message } - 接口分类:
/api/下按模块划分 - 接口版本控制:
/api/v1/ - 接口文档:使用Swagger生成API文档
2. 安全实践
- 强制HTTPS
- 使用JWT进行认证
- 设置CORS策略
- 使用CSRF防护
- 对敏感接口进行速率限制
3. 性能优化
- 使用缓存策略(本地/服务端)
- 合并重复请求
- 使用请求节流
- 使用预加载策略
- 使用懒加载策略
4. 异常处理
- 统一的错误处理逻辑
- 错误日志记录
- 错误分类处理(网络错误、业务错误)
- 错误重试机制
十一、总结
在nuxt.js中使用axios及其二次封装,需要考虑以下核心要素:
- 统一性:通过拦截器实现请求和响应的统一处理
- 安全性:正确处理认证、授权、CORS等安全机制
- 可维护性:良好的封装结构便于后续维护
- 性能优化:通过缓存、合并请求等手段提升性能
- 错误处理:完善的错误处理机制提升健壮性
在实际项目中,建议:
- 对所有API接口进行统一封装
- 使用拦截器处理认证和错误
- 根据业务需求选择合适的缓存策略
- 对关键接口进行性能优化
- 保持良好的代码结构和文档
需要注意的是,这种封装方案适合中大型项目,对于小型项目或简单功能,直接使用axios可能更简单直接。同时,在涉及复杂业务逻辑时,需要根据具体情况调整封装策略。