--- description: "小程序RPC客户端开发规范 - 基于Taro + Hono RPC的完整实现指南" --- # 小程序RPC开发规范 ## 概述 本文档定义了小程序端使用Taro框架结合Hono RPC客户端的标准开发规范。基于现有的`mini/src/api.ts`、`mini/src/utils/rpc-client.ts`和`mini/src/pages/login/wechat-login.tsx`中的最佳实践。 ## 核心架构 ### 1. RPC客户端配置 #### 1.1 客户端初始化 (`mini/src/utils/rpc-client.ts`) ```typescript // 环境配置 const API_BASE_URL = process.env.TARO_APP_API_BASE_URL || 'http://localhost:3000' // 自定义fetch适配Taro.request const taroFetch: any = async (input, init) => { const url = typeof input === 'string' ? input : input.url const method = init.method || 'GET' const requestHeaders: Record = init.headers; // 自动设置content-type const keyOfContentType = Object.keys(requestHeaders).find(item => item.toLowerCase() === 'content-type') if (!keyOfContentType) { requestHeaders['content-type'] = 'application/json' } // 构建Taro请求选项 const options: Taro.request.Option = { url, method: method as any, data: init.body, header: requestHeaders } // 自动添加token认证 const token = Taro.getStorageSync('mini_token') if (token) { options.header = { ...options.header, 'Authorization': `Bearer ${token}` } } try { const response = await Taro.request(options) // 处理响应数据 const body = response.statusCode === 204 ? null : responseHeaders['content-type']!.includes('application/json') ? JSON.stringify(response.data) : response.data; return new ResponsePolyfill( body, { status: response.statusCode, statusText: response.errMsg || 'OK', headers: responseHeaders } ) } catch (error) { console.error('API Error:', error) Taro.showToast({ title: error.message || '网络错误', icon: 'none' }) throw error } } // 创建Hono RPC客户端工厂函数 export const rpcClient = () => { return hc(`${API_BASE_URL}`, { fetch: taroFetch }) } ``` #### 1.2 客户端API定义 (`mini/src/api.ts`) ```typescript import type { AuthRoutes, UserRoutes, RoleRoutes, FileRoutes } from '@/server/api' import { rpcClient } from './utils/rpc-client' // 创建各个模块的RPC客户端 export const authClient = rpcClient().api.v1.auth export const userClient = rpcClient().api.v1.users export const roleClient = rpcClient().api.v1.roles export const fileClient = rpcClient().api.v1.files ``` ## 使用规范 ### 2.1 调用方式 #### 标准GET请求 ```typescript const response = await userClient.$get({ query: { page: 1, pageSize: 10 } }) ``` #### POST请求(带请求体) ```typescript const response = await authClient['mini-login'].$post({ json: { code: loginRes.code, userInfo: userProfile.userInfo } }) ``` #### 带路径参数的请求 ```typescript const response = await userClient[':id'].$get({ param: { id: userId } }) ``` ### 2.2 响应处理规范 #### 成功响应处理 ```typescript if (response.status === 200) { const { token, user, isNewUser } = await response.json() // 保存token到本地存储 Taro.setStorageSync('mini_token', token) Taro.setStorageSync('userInfo', user) // 显示成功提示 Taro.showToast({ title: isNewUser ? '注册成功' : '登录成功', icon: 'success', duration: 1500 }) } ``` #### 错误响应处理 ```typescript try { const response = await authClient['mini-login'].$post({ json: { code, userInfo } }) if (response.status !== 200) { const errorData = await response.json() throw new Error(errorData.message || '操作失败') } } catch (error: any) { const errorMessage = error.message || '网络错误' // 分类处理错误 if (errorMessage.includes('用户拒绝授权')) { Taro.showModal({ title: '提示', content: '需要授权才能使用小程序的全部功能', showCancel: false }) } else { Taro.showToast({ title: errorMessage, icon: 'none', duration: 3000 }) } } ``` ## 微信小程序特殊场景 ### 3.1 微信登录流程 基于`mini/src/pages/login/wechat-login.tsx`的最佳实践: ```typescript const handleWechatLogin = async () => { try { Taro.showLoading({ title: '登录中...', mask: true }) // 1. 获取用户信息授权 const userProfile = await Taro.getUserProfile({ desc: '用于完善用户资料' }) // 2. 获取登录code const loginRes = await Taro.login() if (!loginRes.code) { throw new Error('获取登录凭证失败') } // 3. 调用RPC接口 const response = await authClient['mini-login'].$post({ json: { code: loginRes.code, userInfo: userProfile.userInfo } }) Taro.hideLoading() if (response.status === 200) { const { token, user, isNewUser } = await response.json() // 4. 保存登录态 Taro.setStorageSync('userInfo', user) Taro.setStorageSync('mini_token', token) // 5. 跳转页面 Taro.switchTab({ url: '/pages/index/index' }) } } catch (error) { Taro.hideLoading() // 错误处理... } } ``` ### 3.2 平台检测 ```typescript import { isWeapp } from '@/utils/platform' // 检查是否为微信小程序环境 const wechatEnv = isWeapp() if (!wechatEnv) { Taro.showModal({ title: '提示', content: '微信登录功能仅支持在微信小程序中使用', showCancel: false }) } ``` ## 开发规范 ### 4.1 文件结构 ``` mini/ ├── src/ │ ├── api.ts # RPC客户端定义 │ ├── utils/ │ │ └── rpc-client.ts # RPC客户端工厂 │ └── pages/ │ └── [功能页面]/ │ └── index.tsx # 页面逻辑 ``` ### 4.2 命名规范 - **客户端命名**:`[模块名]Client`(如`authClient`、`userClient`) - **方法命名**:遵循RESTful规范(如`$get`、`$post`、`$put`、`$delete`) - **路径命名**:使用小写字母和连字符(如`mini-login`) ### 4.3 类型安全 ```typescript // 使用InferResponseType提取响应类型 import type { InferResponseType } from 'hono/client' type LoginResponse = InferResponseType // 使用InferRequestType提取请求类型 import type { InferRequestType } from 'hono/client' type LoginRequest = InferRequestType['json'] ``` ### 4.4 环境配置 在`mini/.env`中配置API地址: ```bash TARO_APP_API_BASE_URL=https://your-api-domain.com ``` ## 最佳实践 ### 5.1 请求封装 ```typescript // 创建通用请求hook const useApiRequest = () => { const [loading, setLoading] = useState(false) const request = async ( apiCall: () => Promise, successCallback?: (data: T) => void, errorCallback?: (error: Error) => void ) => { setLoading(true) try { const response = await apiCall() const data = await response.json() if (response.status === 200) { successCallback?.(data) } else { throw new Error(data.message || '请求失败') } } catch (error) { errorCallback?.(error) } finally { setLoading(false) } } return { loading, request } } ``` ### 5.2 错误处理 ```typescript const handleApiError = (error: any) => { const message = error.message || '网络错误' // 网络错误 if (message.includes('Network') || message.includes('网络')) { Taro.showModal({ title: '网络错误', content: '请检查网络连接后重试', showCancel: false }) return } // 业务错误 Taro.showToast({ title: message, icon: 'none', duration: 3000 }) } ``` ### 5.3 加载状态管理 ```typescript const [loading, setLoading] = useState(false) const handleRequest = async () => { setLoading(true) Taro.showLoading({ title: '加载中...' }) try { const response = await apiClient.method.$post({ json: data }) // 处理响应... } finally { Taro.hideLoading() setLoading(false) } }