1. Nuxt3模块化API架构设计解析
在开发中大型Nuxt3项目时,API层的组织方式直接影响项目的可维护性和扩展性。传统的前端项目中,API调用往往散落在各个组件中,缺乏统一管理和错误处理机制。而基于Composable的模块化API架构,能够很好地解决这些问题。
1.1 核心设计思路
这个架构的核心思想是将API请求抽象为三个层次:
- 基础层(useApi.js):使用Nuxt3的Composable特性封装底层HTTP请求,提供统一的错误处理、Token管理和请求日志
- 业务模块层(api/modules/):按业务领域划分API模块,每个模块基于useApi实现具体接口
- 统一导出层(api/api.js):集中管理所有业务模块的导出,保持组件调用方式的简洁性
这种分层设计借鉴了后端开发中的"分层架构"思想,将关注点分离,使代码更易于维护和测试。
1.2 为什么选择Composable模式
Nuxt3大力推广Composable作为代码复用的主要方式,相比传统的工具类或Mixin有以下优势:
- 自动注入上下文:可以访问Nuxt提供的运行时配置、环境变量等
- 更好的类型支持:配合TypeScript能提供完整的类型推断
- 响应式集成:可以方便地与Vue的响应式系统结合
- 按需加载:只在需要时引入,不会增加初始包体积
提示:在Nuxt3项目中,所有放置在composables目录下的文件都会自动导入,无需手动引入,这是Nuxt3的约定优于配置原则的体现。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 基础层实现详解
2.1 useApi.js核心结构
让我们深入分析useApi.js的实现细节:
javascript复制export const useApi = () => {
// 1. 配置初始化
const config = useRuntimeConfig()
const API_BASE_URL = config.public.expressApiUrl || 'http://localhost:3001'
// 2. Token管理
const getToken = () => { /* ... */ }
// 3. 请求选项配置
const createRequestOptions = (customOptions = {}) => { /* ... */ }
// 4. 核心请求方法
const request = async (config) => { /* ... */ }
// 5. HTTP方法快捷方式
const get = (endpoint, params) => request({ /* ... */ })
const post = (endpoint, data) => request({ /* ... */ })
// ...其他HTTP方法
// 6. 导出接口
return {
get,
post,
// ...
tokenManager,
apiFetch
}
}
2.2 关键实现细节
2.2.1 请求配置管理
createRequestOptions方法负责创建统一的请求配置:
javascript复制const createRequestOptions = (customOptions = {}) => {
const token = getToken()
return {
baseURL: API_BASE_URL,
timeout: 10000, // 10秒超时
headers: {
'Content-Type': 'application/json',
...(token && { Authorization: `Bearer ${token}` })
},
// 错误处理回调
onRequestError: (error) => { /* ... */ },
onResponseError: ({ response, error }) => { /* ... */ }
}
}
这里有几个值得注意的点:
- 超时设置:默认10秒超时,防止请求长时间挂起
- 自动Token注入:如果存在Token会自动添加到请求头
- 统一错误处理:对所有请求错误进行集中处理
2.2.2 请求日志记录
开发环境下自动记录请求和响应日志:
javascript复制if (import.meta.client && window.location.hostname === 'localhost') {
console.log(`🚀 API Request: ${method?.toUpperCase()} ${url}`, {
params,
data,
})
}
这个特性在调试API时非常有用,可以清晰看到请求的发送和返回情况。
3. 业务模块实现
3.1 模块化组织方式
业务API按功能模块划分,例如:
code复制api/modules/
├── projects.js # 项目管理相关API
├── categories.js # 分类管理相关API
├── users.js # 用户管理相关API
└── api.js # 统一导出入口
每个模块都基于useApi实现,保持一致的调用方式。
3.2 典型模块实现示例
以projects.js为例:
javascript复制// api/modules/projects.js
import { useApi } from '../composables/useApi'
const api = useApi()
export const getProjects = async (params = {}) => {
return api.get('/projects', params)
}
export const createProject = async (projectData) => {
return api.post('/projects', projectData)
}
export const updateProject = async (id, updateData) => {
return api.put(`/projects/${id}`, updateData)
}
export const deleteProject = async (id) => {
return api.del(`/projects/${id}`)
}
3.3 统一导出管理
在api.js中集中导出所有模块:
javascript复制// api/api.js
export * from './modules/projects'
export * from './modules/categories'
export * from './modules/users'
这样在组件中可以按需导入:
javascript复制import { getProjects, createProject } from '~/api'
4. 组件中的使用方式
4.1 基本调用示例
在Vue组件中使用API非常简单:
javascript复制<script setup>
import { getProjects } from '~/api/projects'
const loading = ref(false)
const projects = ref([])
const fetchProjects = async () => {
try {
loading.value = true
projects.value = await getProjects({
page: 1,
limit: 10,
status: 'active'
})
} catch (error) {
console.error('获取项目列表失败:', error)
// 这里可以显示错误提示
} finally {
loading.value = false
}
}
// 初始化加载
onMounted(() => {
fetchProjects()
})
</script>
4.2 组合式API最佳实践
结合Composition API可以实现更优雅的代码组织:
javascript复制// composables/useProjects.js
import { getProjects } from '~/api/projects'
export const useProjects = () => {
const projects = ref([])
const loading = ref(false)
const error = ref(null)
const fetchProjects = async (params = {}) => {
try {
loading.value = true
error.value = null
projects.value = await getProjects(params)
} catch (err) {
error.value = err
} finally {
loading.value = false
}
}
return {
projects,
loading,
error,
fetchProjects
}
}
然后在组件中使用:
javascript复制<script setup>
const { projects, loading, error, fetchProjects } = useProjects()
onMounted(() => {
fetchProjects()
})
</script>
5. 高级功能与扩展
5.1 请求拦截与转换
可以在useApi中添加请求/响应拦截器,对数据进行统一处理:
javascript复制const request = async (config) => {
// 请求前处理
if (config.transformRequest) {
config.data = config.transformRequest(config.data)
}
const response = await ofetch(fullUrl, options)
// 响应后处理
if (config.transformResponse) {
return config.transformResponse(response)
}
return response
}
5.2 类型安全增强
结合TypeScript可以提供更好的类型支持:
typescript复制// types/api.d.ts
interface ApiResponse<T> {
code: number
data: T
message?: string
}
interface Project {
id: string
name: string
// ...
}
// api/modules/projects.ts
export const getProjects = async (
params?: PaginationParams
): Promise<ApiResponse<Project[]>> => {
return api.get('/projects', params)
}
5.3 性能优化技巧
- 请求去重:对于相同的请求可以缓存Promise,避免重复发送
- 取消请求:实现请求取消功能,避免组件卸载后仍处理响应
- 缓存策略:对GET请求实现内存缓存,减少不必要的网络请求
6. 常见问题与解决方案
6.1 跨域问题处理
如果在开发环境遇到跨域问题,可以通过以下方式解决:
- 配置Nuxt3的代理:
javascript复制// nuxt.config.ts
export default defineNuxtConfig({
// ...
nitro: {
devProxy: {
'/api': {
target: 'http://your-api-server.com',
changeOrigin: true
}
}
}
})
- 在useApi中修改baseURL:
javascript复制const API_BASE_URL = process.dev ? '/api' : 'https://your-api-server.com'
6.2 Token失效处理
当Token失效时,可以跳转到登录页:
javascript复制onResponseError: ({ response }) => {
if (response.status === 401) {
if (process.client) {
localStorage.removeItem('token')
navigateTo('/login')
}
}
}
6.3 请求重试机制
对于某些临时性错误,可以实现自动重试:
javascript复制const requestWithRetry = async (config, retries = 3) => {
try {
return await request(config)
} catch (error) {
if (retries > 0 && isRetryableError(error)) {
await new Promise(resolve => setTimeout(resolve, 1000))
return requestWithRetry(config, retries - 1)
}
throw error
}
}
7. 测试策略
7.1 单元测试
使用vitest测试useApi:
javascript复制import { useApi } from '@/composables/useApi'
describe('useApi', () => {
it('should make GET request', async () => {
const { get } = useApi()
const mockData = { id: 1, name: 'Test' }
global.fetch = vi.fn(() =>
Promise.resolve({
json: () => Promise.resolve(mockData)
})
)
const result = await get('/test')
expect(result).toEqual(mockData)
})
})
7.2 集成测试
测试业务模块与组件的集成:
javascript复制import { getProjects } from '@/api/projects'
describe('projects API', () => {
it('should fetch projects', async () => {
const mockProjects = [{ id: 1, name: 'Project 1' }]
vi.spyOn(api, 'get').mockResolvedValue(mockProjects)
const result = await getProjects()
expect(result).toEqual(mockProjects)
})
})
8. 部署注意事项
8.1 生产环境配置
确保生产环境配置正确:
javascript复制// .env
NUXT_PUBLIC_EXPRESS_API_URL=https://your-production-api.com
8.2 性能监控
添加性能监控代码:
javascript复制const request = async (config) => {
const start = Date.now()
try {
const response = await ofetch(fullUrl, options)
const duration = Date.now() - start
trackApiPerformance(config.method, config.url, duration)
return response
} catch (error) {
trackApiError(config.method, config.url, error)
throw error
}
}
9. 架构演进方向
随着项目规模扩大,可以考虑以下优化:
- API版本管理:在baseURL中加入版本号(如/v1/)
- 请求优先级:实现请求优先级队列
- 离线缓存:添加Service Worker支持离线访问
- GraphQL支持:在现有架构上扩展GraphQL能力
这种模块化API架构已经在多个生产项目中得到验证,能够很好地平衡开发效率和维护成本。特别是在团队协作项目中,统一的API调用方式大大降低了沟通成本。
