1. BuildAdmin接口封装在UniApp中的核心价值
作为一名长期奋战在一线的全栈开发者,我经历过太多前后端协作的痛点了。特别是在UniApp这种跨平台场景下,每个API调用都要处理不同平台的兼容性问题,就像在十个鸡蛋上跳舞——稍有不慎就会弄得一地狼藉。而BuildAdmin的接口封装方案,恰好给了我们一个稳固的跳舞平台。
这个方案最吸引我的地方在于它用三层架构解决了跨平台开发的三大难题:首先是统一了不同运行环境的请求适配,无论是微信小程序、H5还是App,都能用同一套代码;其次是通过拦截器机制实现了全局的权限控制和错误处理,省去了每个页面重复写鉴权逻辑的麻烦;最重要的是提供了完善的TypeScript类型支持,让接口调用从"盲人摸象"变成"明镜高悬"。
2. 环境搭建与基础配置
2.1 初始化UniApp项目
我推荐使用Vue3+TypeScript模板创建项目,这是目前最稳定的组合:
bash复制npm install -g @vue/cli
vue create -p dcloudio/uni-preset-vue my-project
选择"默认模板(TypeScript)"后,需要特别注意几个配置项:
- 在
manifest.json中启用"transformPx"选项,解决不同平台尺寸适配问题 - 在
tsconfig.json中添加"skipLibCheck": true,避免类型声明冲突 - 安装必要的polyfill:
bash复制npm install core-js@3 regenerator-runtime
2.2 BuildAdmin接口层安装
BuildAdmin的HTTP模块需要单独引入:
bash复制npm install @buildadmin/http-request
然后在src/utils下创建request.ts,这是我调整过的推荐配置:
typescript复制import { createRequest } from '@buildadmin/http-request'
import type { RequestConfig } from '@buildadmin/http-request/types'
const request = createRequest({
baseURL: process.env.VUE_APP_API_BASE,
timeout: 15000,
withCredentials: true,
interceptors: {
requestInterceptor: (config) => {
const token = store.getters.token
if (token) {
config.headers!['Authorization'] = `Bearer ${token}`
}
return config
},
responseInterceptor: (response) => {
// 处理UniApp返回的数据结构差异
const res = response.data
if (res.code !== 200) {
uni.showToast({ title: res.message, icon: 'none' })
return Promise.reject(new Error(res.message || 'Error'))
}
return res
}
}
})
export default request
3. 接口封装实战技巧
3.1 RESTful风格接口规范
在实际项目中,我建议采用模块化封装方式。创建src/api目录,按业务划分接口文件。以用户模块为例:
typescript复制// src/api/user.ts
import request from '@/utils/request'
import type { UserInfo, LoginParams } from './types'
export const login = (data: LoginParams) => {
return request<{ token: string }>({
url: '/auth/login',
method: 'POST',
data
})
}
export const getUserInfo = () => {
return request<UserInfo>({
url: '/user/info',
method: 'GET'
})
}
配套的类型定义文件src/api/types.ts:
typescript复制export interface LoginParams {
username: string
password: string
captcha?: string
}
export interface UserInfo {
id: number
name: string
avatar: string
roles: string[]
}
3.2 文件上传特殊处理
UniApp的文件上传需要特殊处理,这是我总结的最佳实践:
typescript复制export const uploadFile = (filePath: string) => {
return new Promise((resolve, reject) => {
uni.uploadFile({
url: `${process.env.VUE_APP_API_BASE}/upload`,
filePath,
name: 'file',
header: {
'Authorization': `Bearer ${store.getters.token}`
},
success: (res) => {
if (res.statusCode === 200) {
resolve(JSON.parse(res.data))
} else {
reject(new Error(res.data))
}
},
fail: (err) => {
reject(err)
}
})
})
}
4. 跨平台适配解决方案
4.1 平台差异处理策略
不同平台的网络请求存在微妙差异,这是我整理的兼容性处理表格:
| 问题场景 | 微信小程序 | H5 | App | 解决方案 |
|---|---|---|---|---|
| Cookie处理 | 默认不携带 | 正常 | 正常 | 配置withCredentials: true |
| 响应数据类型 | 自动JSON.parse | 需手动处理 | 需手动处理 | 统一在拦截器处理 |
| 超时错误捕获 | 特殊错误码 | 常规错误 | 常规错误 | 错误拦截器统一转换 |
| 请求并发限制 | 10个 | 无限制 | 无限制 | 实现请求队列管理 |
4.2 网络状态检测增强
在移动端必须考虑弱网情况,这是我封装的网络检测工具:
typescript复制// src/utils/network.ts
export const checkNetwork = () => {
return new Promise((resolve) => {
uni.getNetworkType({
success: (res) => {
if (res.networkType === 'none') {
uni.showModal({
title: '网络提示',
content: '当前无网络连接',
showCancel: false
})
resolve(false)
} else {
resolve(true)
}
}
})
})
}
// 在请求拦截器中加入
interceptors: {
requestInterceptor: async (config) => {
const isConnected = await checkNetwork()
if (!isConnected) {
return Promise.reject(new Error('network disconnected'))
}
// ...原有逻辑
}
}
5. 性能优化与安全实践
5.1 请求缓存策略
对于频繁调用且数据变化不频繁的接口,建议实现缓存机制:
typescript复制const cacheMap = new Map()
export const cachedRequest = (config: RequestConfig, cacheKey: string, expire = 300000) => {
const now = Date.now()
const cached = cacheMap.get(cacheKey)
if (cached && now - cached.timestamp < expire) {
return Promise.resolve(cached.data)
}
return request(config).then(res => {
cacheMap.set(cacheKey, {
data: res,
timestamp: now
})
return res
})
}
5.2 接口安全加固
针对常见的安全威胁,我建议实施以下防护措施:
- 参数签名防篡改:
typescript复制import md5 from 'crypto-js/md5'
const generateSign = (params: Record<string, any>, secret: string) => {
const sortedParams = Object.keys(params).sort().map(k => `${k}=${params[k]}`).join('&')
return md5(`${sortedParams}&key=${secret}`).toString()
}
- 请求频率限制:
typescript复制const requestQueue = new Map()
export const rateLimitedRequest = (config: RequestConfig) => {
const key = `${config.method}-${config.url}`
const lastRequest = requestQueue.get(key) || 0
const now = Date.now()
if (now - lastRequest < 1000) {
return Promise.reject(new Error('请求过于频繁'))
}
requestQueue.set(key, now)
return request(config)
}
6. 调试与错误排查指南
6.1 开发环境日志配置
在request.ts中添加调试拦截器:
typescript复制const request = createRequest({
// ...其他配置
interceptors: {
// ...已有拦截器
responseInterceptor: (response) => {
if (process.env.NODE_ENV === 'development') {
console.groupCollapsed(`%c ${response.config.method} ${response.config.url}`, 'color: #1890ff')
console.log('请求参数:', response.config.params || response.config.data)
console.log('响应数据:', response.data)
console.groupEnd()
}
// ...原有处理逻辑
}
}
})
6.2 常见问题解决方案
根据我的踩坑经验,整理这些典型问题的处理方式:
-
微信小程序证书问题:
- 现象:开发工具正常但真机报错
- 解决:确保后端配置了合法的HTTPS证书,包括中间证书
-
iOS内容安全策略:
- 现象:App Store审核被拒
- 解决:在
manifest.json中添加:json复制"ios": { "networkTimeout": { "request": 15000 }, "secure": { "http": false } }
-
Android 9+明文传输限制:
- 修改
android/app/src/main/res/xml/network_security_config.xml:xml复制<?xml version="1.0" encoding="utf-8"?> <network-security-config> <domain-config cleartextTrafficPermitted="true"> <domain includeSubdomains="true">your.domain.com</domain> </domain-config> </network-security-config>
- 修改
7. 工程化进阶实践
7.1 自动化Mock方案
结合BuildAdmin的Mock功能,实现开发阶段数据模拟:
- 安装依赖:
bash复制npm install mockjs @buildadmin/mock -D
- 创建
mock/user.ts:
typescript复制import { MockMethod } from '@buildadmin/mock'
import { Random } from 'mockjs'
export default [
{
url: '/api/user/info',
method: 'get',
response: () => {
return {
code: 200,
data: {
id: Random.id(),
name: Random.cname(),
avatar: Random.image('100x100'),
roles: ['admin']
}
}
}
}
] as MockMethod[]
- 在
vue.config.js中启用:
javascript复制const { defineConfig } = require('@vue/cli-service')
const mockServer = require('@buildadmin/mock/lib/createMockServer')
module.exports = defineConfig({
// ...其他配置
devServer: {
before(app) {
if (process.env.VUE_APP_ENV === 'mock') {
mockServer(app, {
watch: true,
mockDir: './mock'
})
}
}
}
})
7.2 接口文档自动化
使用Swagger UI结合TypeScript类型生成文档:
- 安装swagger-typescript-api:
bash复制npm install swagger-typescript-api -D
- 添加生成脚本:
json复制{
"scripts": {
"gen:api": "swagger-typescript-api -p https://your.api.com/v2/api-docs -o src/api -n generated.ts"
}
}
- 创建API版本管理策略:
typescript复制// src/api/version.ts
export class ApiVersion {
private static instance: ApiVersion
private versions: Record<string, any> = {}
private constructor() {}
static getInstance() {
if (!ApiVersion.instance) {
ApiVersion.instance = new ApiVersion()
}
return ApiVersion.instance
}
register(version: string, api: any) {
this.versions[version] = api
}
get(version = 'v1') {
return this.versions[version] || this.versions['v1']
}
}
// 使用示例
import { ApiVersion } from './version'
import * as v1 from './generated-v1'
import * as v2 from './generated-v2'
const apiManager = ApiVersion.getInstance()
apiManager.register('v1', v1)
apiManager.register('v2', v2)
export const api = apiManager.get(process.env.VUE_APP_API_VERSION)
