ai-c/小程序前端开发与后端API对接经验汇总.md
2026-02-02 18:21:32 +08:00

220 lines
8.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 小程序前端开发与后端 API 对接经验汇总
适用范围:本仓库小程序前端(`qianduan/qianduan-code/miniprogram`)。目标是让新同学能在不踩坑的情况下完成页面开发、样式对齐和后端 API 对接。
## 1. 项目结构与入口
- 小程序目录:`qianduan/qianduan-code/miniprogram`
- 全局入口:`app.js / app.json / app.wxss`
- 页面目录:`pages/*`
- 通用能力:
- 请求封装:`utils/api.js`
- 登录态:`utils/auth.js`
- 统一错误处理:`utils/errorHandler.js`
- 图片 URL 处理:`utils/imageUrl.js`
- 环境与常量:`config/index.js`
## 2. 环境与 baseURL非常关键
后端 API 的 baseURL 由 `config/index.js` 管理:
- `ENV.{development,staging,production}.API_BASE_URL`
- `CURRENT_ENV`
- `REQUEST_TIMEOUT`(默认 30s
- `PAGE_SIZE`(默认 20
- `STORAGE_KEYS`token/user 等 storage key 的唯一来源)
建议实践:
- 开发阶段用 `development`,发版/线上联调切 `production`
- 不要在页面里硬编码域名或 `/api` 前缀,一律走 `config.API_BASE_URL`
另外,`app.js` 会把 `config.API_BASE_URL` 去掉 `/api` 后写到 `globalData.baseUrl` 并落地到 `storage.baseUrl`,主要给 `utils_new` 那套请求封装使用。正常业务开发推荐统一使用 `utils/api.js` 这一套,避免 token key 和 baseUrl 来源混乱。
## 3. 请求封装与 header 格式(统一约定)
### 3.1 推荐做法:只用 `utils/api.js`
`utils/api.js` 内部封装了 `request()`,完成以下事情:
- URL 拼接:`config.API_BASE_URL + url`
- header 默认包含:
- `Content-Type: application/json`
- `Authorization: Bearer <token>`token 从 `wx.getStorageSync(config.STORAGE_KEYS.TOKEN)` 取)
- 401未登录/登录过期)处理:
- 非 silent 模式会清理本地登录信息token/user/userId/expiry
- 同步 `app.globalData.isLoggedIn = false`
- 尝试调用当前页 `onAuthRequired()`(如果页面实现了该方法)
- `silent` 模式用于不希望打断用户操作的接口401 时不清本地登录态,只 reject
页面层调用建议:
```js
import api from '../../utils/api'
import { handleApiError } from '../../utils/errorHandler'
Page({
async onLoad() {
try {
wx.showLoading({ title: '加载中' })
const res = await api.user.getProfile()
this.setData({ profile: res.data })
} catch (err) {
handleApiError(err)
} finally {
wx.hideLoading()
}
}
})
```
### 3.2 不推荐混用:`utils_new/request.js`
仓库里还有一套 `utils_new/request.js`(更偏调试/可切 baseUrl但它使用的 token key 是写死的 `auth_token`,而主体系使用 `config.STORAGE_KEYS.TOKEN`(同为 `auth_token` 但请以配置为准)。混用会导致以下问题:
- 你以为登录了,实际请求没带对 token
- baseUrl 读取路径不同,导致部分页面请求走了另一个域名/端口
结论:业务开发优先只用 `utils/api.js`;除非明确是在做本地联调/临时调试,并保证 token/baseUrl 与主体系一致。
## 4. 登录态与鉴权(页面开发常见坑)
核心逻辑在 `utils/auth.js``app.js`
- `app.js` 启动会调用 `checkLoginStatus()`
- 先本地检查 token
- 再调用 `auth.verifyLogin()` 请求服务端 `/auth/me` 做校验
- 网络异常时可能允许用本地缓存 userInfo 继续使用(提升弱网体验)
- 页面若必须登录才能访问,建议用 `auth.ensureLogin()` 做统一校验,校验失败会跳登录页
- token/用户信息写入请走 `auth.saveUserInfo(user, token, expiresAt)`
页面侧最佳实践:
- 需要登录的页面,在 `onLoad/onShow` 里先 `await auth.ensureLogin()` 再拉数据
- 有接口需要“静默拉取”(比如后台刷新余额),用 `api.request(url, { silent: true })` 或 API 方法暴露的 silent 选项(按现有实现为准)
- 需要 401 时做自定义交互的页面,提供 `onAuthRequired()` 方法(例如弹窗提示/引导登录),否则默认行为是清理登录态并由页面自行处理后续
## 5. 统一错误处理(减少页面重复代码)
`utils/errorHandler.js` 提供了:
- `handleApiError(err)`:统一 toast/提示文案
- `retryOnError(fn, { maxRetries, retryDelay })`:可重试逻辑(认证错误不会重试)
建议实践:
- 页面 catch 里优先调用 `handleApiError(err)`,不要每个页面自己写一套 toast 文案
- 对用户关键路径(下单/提现提交):
- 网络错误提示要明确
- 禁止无限重试
## 6. 分页与列表加载(约定优先)
当前工程的分页没有抽象成统一 `paginate()`更多是“API 方法里给默认分页参数 + 页面侧传参”:
- 常见参数:`page` + `pageSize`(或部分接口用 `limit`
- 默认值可参考 `config.PAGE_SIZE`
建议实践:
- 页面 data 里维护:`page, pageSize, list, loading, hasMore`
- `onReachBottom` 时判 `hasMore && !loading` 再请求下一页
- 后端返回是否还有更多,以返回字段为准(如果没有统一字段,就以 `list.length < pageSize` 推断)
## 7. 上传与图片 URL最容易漏 token
### 7.1 上传
使用 `utils/api.js``uploadFile()`
- 上传地址:`${config.API_BASE_URL}/upload`
- 携带 `Authorization: Bearer <token>`
- 支持 `formData.folder` 指定目录
- 兼容多种返回格式(`{code:0}` 或 `{success:true}`
### 7.2 图片 URL 拼接
后端返回相对路径时,用 `utils/imageUrl.js``getFullImageUrl()` 拼成完整地址(基于 `API_BASE_URL` 去掉 `/api`)。
## 8. 头部与页面结构(统一样式框架)
工程里常见的头部结构是“固定导航 + 状态栏占位 + 标题 + 返回按钮”:
- 导航容器:`nav-container`
- 状态栏占位:`status-bar`
- 导航栏:`nav-bar`
- 返回按钮:`nav-back`
- 标题:`nav-title`
常见页面结构示例可参考:
- 提现页:`pages/withdraw/withdraw.wxml`、`pages/withdraw/withdraw.wxss`
- 充值页:`pages/recharge/recharge.wxml`
- 提现记录:`pages/withdraw-records/withdraw-records.wxml`
建议实践:
- 导航容器固定fixed内容区通过 `padding-top: {{totalNavHeight}}px``padding-top: {{totalNavHeight + n}}px` 避免被遮挡
- 页面根节点常用:`<view class="page safe-bottom">`,保证底部安全区
## 9. 整体样式风格(如何保持一致)
### 9.1 全局设计令牌Design Tokens
`app.wxss` 定义了全局 CSS 变量(建议优先使用):
- `--primary`、`--primary-light`
- `--foreground`、`--muted`、`--border`
- `--radius`
并补齐了全局基础类:
- `.btn-reset`:用于 button去掉默认边框与默认点击态差异
- `.safe-bottom`:适配底部安全区
### 9.2 页面级常用视觉语言
在本项目里,“一致感”主要来自以下元素:
- 背景:浅紫/粉色系渐变或纯色(如 `#E8C3D4`、`linear-gradient(180deg, #F8F5FF 0%, #FFFFFF 100%)`
- 卡片:白底 + 大圆角20rpx~48rpx+ 轻阴影 + 适度边框
- 主按钮:紫色渐变(`#B06AB3 → #9B4D9E`+ 胶囊圆角999rpx+ 统一阴影
- 文案层级标题更粗更深700~900说明文字更浅`#6B7280/#9CA3AF`
建议做法:
- 新页面先找一个“风格相近”的现有页面抄结构与基础样式,再替换业务内容
- 少做随意的颜色与圆角,优先复用现有渐变、阴影、字号层级
## 10. 交互与组件(避免嵌套交互坑)
约定:交互元素不要互相嵌套(例如可点击容器里再放 button 或另一个可点击 view。如果需要整块可点
- 要么外层用 `bindtap`,内部不用 button用普通 view/text 模拟按钮)
- 要么使用 button 做唯一点击源,外层不再绑事件
这类嵌套会在检查工具/规范中触发警告,也容易造成点击穿透/事件冒泡问题。
## 11. 新增/修改 API 的推荐流程
1.`utils/api.js` 增加/修改对应模块方法(保持命名与路径一致)
2. 请求一律通过内部 `request()`(确保 header、401、timeout、错误格式一致
3. 页面侧只调用 `api.xxx.yyy()`,不直接拼 URL、不直接 `wx.request`
4. 异常统一走 `handleApiError`,少在页面写自定义错误文案
## 12. 排查清单(定位问题最快)
- 请求没到后端:
- `config.CURRENT_ENV` 是否正确
- `config.API_BASE_URL` 是否正确(是否带了 `/api`
- 是否混用了 `utils_new` 导致 baseUrl/token 读取来源不一致
- 401/未登录:
- storage 里是否存在 `config.STORAGE_KEYS.TOKEN`
- 页面是否需要 `auth.ensureLogin()`
- 是否误用了 silent 导致 401 没触发清理/引导
- 样式不一致:
- 是否使用了 `.btn-reset`button 默认样式会把你“设计稿一致性”破坏掉)
- 是否使用了 `.safe-bottom`
- 是否沿用了已有页面的卡片/按钮视觉语言