本文档说明如何将前端模拟数据替换为真实后端API接口调用。目前前端使用模拟数据进行开发,后端接口开发完成后,需要按照本指南进行替换。
/api/homepage.js
)├── api/
│ └── homepage.js # ⚠️ 需要替换
├── mock/
│ └── mockData.js # 🗑️ 后期可删除
├── pages/
│ └── index/index.vue # ✅ 无需修改
└── docs/
└── API_REPLACEMENT_GUIDE.md
/api/homepage.js
)当前模拟代码:
import { MOCK_HOMEPAGE_DATA } from '@/mock/mockData.js'
export const getHomepageData = () => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(MOCK_HOMEPAGE_DATA)
}, 300)
})
}
替换为真实API:
export const getHomepageData = () => {
return uni.request({
url: `${API_BASE_URL}/api/homepage/data`,
method: 'GET',
header: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${getToken()}` // 如果需要认证
},
timeout: 10000
}).then(([error, res]) => {
if (error) {
throw error
}
return res.data
})
}
在文件顶部添加:
// API基础配置
const API_BASE_URL = 'https://your-api-domain.com' // 替换为实际域名
// 获取认证token
const getToken = () => {
return uni.getStorageSync('token') || ''
}
按照相同模式替换所有 API 方法:
getBannerConfig()
getCategories()
getCategoryProducts()
getProductDetail()
getAssetUrl()
searchProducts()
请求:
GET /api/homepage/data
Authorization: Bearer {token}
期望响应格式:
{
"success": true,
"message": "数据获取成功",
"data": {
"banners": [
{
"id": "banner_config_001",
"title": "新春特惠酒水节",
"subtitle": "全场酒水满299减50",
"imageUrl": "https://cdn.example.com/banner1.jpg",
"linkType": "product",
"linkTarget": "product_jg_001",
"status": 1,
"sort": 1
}
],
"categories": [
{
"id": "category_jiugu",
"name": "酒谷酒",
"sort": 1,
"status": 1,
"products": [
{
"id": "product_jg_001",
"name": "酒谷陈酿",
"imageUrl": "https://cdn.example.com/product1.jpg",
"stock": 100,
"sales": 256
}
]
}
]
}
}
请求:
GET /api/config/banners
Authorization: Bearer {token}
期望响应格式:
{
"success": true,
"data": {
"banners": [
{
"id": "banner_config_001",
"title": "新春特惠酒水节",
"assetId": "banner_001",
"imageUrl": "https://cdn.example.com/banner1.jpg",
"linkType": "product",
"linkTarget": "product_jg_001",
"status": 1,
"sort": 1,
"startTime": "2024-01-01 00:00:00",
"endTime": "2024-12-31 23:59:59"
}
]
}
}
请求:
GET /api/categories
Authorization: Bearer {token}
期望响应格式:
{
"success": true,
"data": {
"categories": [
{
"id": "category_jiugu",
"name": "酒谷酒",
"sort": 1,
"status": 1,
"description": "自主品牌酒谷系列"
}
]
}
}
请求:
GET /api/products?categoryId={categoryId}&page={page}&limit={limit}
Authorization: Bearer {token}
期望响应格式:
{
"success": true,
"data": {
"products": [
{
"id": "product_jg_001",
"name": "酒谷陈酿",
"categoryId": "category_jiugu",
"imageUrl": "https://cdn.example.com/product1.jpg",
"status": 1,
"sort": 1,
"stock": 100,
"sales": 256
}
],
"pagination": {
"page": 1,
"limit": 10,
"total": 50
}
}
}
export const handleApiError = (error) => {
console.error('API请求错误:', error)
// 根据实际后端错误码进行处理
const { statusCode, data } = error
switch (statusCode) {
case 401:
// 清除本地token
uni.removeStorageSync('token')
uni.showToast({ title: '登录已过期,请重新登录', icon: 'none' })
// 跳转到登录页
uni.navigateTo({ url: '/pages/auth/login' })
break
case 403:
uni.showToast({ title: '权限不足', icon: 'none' })
break
case 404:
uni.showToast({ title: '请求的资源不存在', icon: 'none' })
break
case 422:
// 参数验证错误
const message = data?.message || '参数错误'
uni.showToast({ title: message, icon: 'none' })
break
case 500:
uni.showToast({ title: '服务器内部错误', icon: 'none' })
break
default:
const errorMessage = data?.message || '网络错误,请稍后重试'
uni.showToast({ title: errorMessage, icon: 'none' })
}
}
如果后端返回的数据结构与前端需要的不完全一致,需要在API层进行映射转换:
export const getHomepageData = async () => {
const response = await uni.request({
// ... 请求配置
})
// 数据结构映射
const mappedData = {
success: response.data.success,
message: response.data.message,
data: {
banners: response.data.data.banners.map(banner => ({
id: banner.id,
title: banner.title,
subtitle: banner.subtitle,
image: banner.imageUrl, // 映射字段名
linkType: banner.linkType,
linkTarget: banner.linkTarget
})),
categories: response.data.data.categories.map(category => ({
id: category.id,
name: category.name,
products: category.products.map(product => ({
id: product.id,
name: product.name,
image: product.imageUrl, // 映射字段名
stock: product.stock,
sales: product.sales
}))
}))
}
}
return mappedData
}
创建 /config/env.js
:
// 环境配置
const ENV_CONFIG = {
development: {
API_BASE_URL: 'http://localhost:8080',
CDN_BASE_URL: 'http://localhost:8080/uploads'
},
production: {
API_BASE_URL: 'https://api.jiugu.com',
CDN_BASE_URL: 'https://cdn.jiugu.com'
}
}
// 根据编译环境自动选择
const env = process.env.NODE_ENV === 'production' ? 'production' : 'development'
export default ENV_CONFIG[env]
import ENV_CONFIG from '@/config/env.js'
const API_BASE_URL = ENV_CONFIG.API_BASE_URL
开启网络请求日志:
// 在 main.js 中添加
uni.addInterceptor('request', {
invoke(args) {
console.log('🚀 API请求:', args)
},
success(args) {
console.log('✅ API成功:', args)
},
fail(err) {
console.log('❌ API失败:', err)
}
})
对比模拟数据和真实数据: 确保数据结构一致,特别关注字段名和数据类型
分阶段替换: 可以先替换一个接口进行测试,确认无误后再替换其他接口
// 添加简单的内存缓存
const cache = new Map()
const CACHE_DURATION = 5 * 60 * 1000 // 5分钟
export const getHomepageData = async () => {
const cacheKey = 'homepage_data'
const cached = cache.get(cacheKey)
if (cached && Date.now() - cached.timestamp < CACHE_DURATION) {
return cached.data
}
const data = await fetchHomepageData()
cache.set(cacheKey, {
data,
timestamp: Date.now()
})
return data
}
在组件中添加图片懒加载:
<image
:src="product.image"
class="product-image"
mode="aspectFill"
lazy-load
></image>
接口替换完成并测试通过后:
删除模拟数据文件:
rm -rf mock/
清理import引用: 检查并移除所有对模拟数据的引用
更新注释: 移除代码中的 "TODO: API替换" 注释
getHomepageData
接口getBannerConfig
接口getCategories
接口getCategoryProducts
接口getProductDetail
接口searchProducts
接口如在API替换过程中遇到问题,请联系: