HarmonyOS APP开发---“菜谱汇“美食菜谱App,需要用到这个库
·
HarmonyOS APP开发—“菜谱汇"美食菜谱App,需要用到这个库> 菜谱数据分散在三个平台,要同时拉取、合并、按评分排序。麻烦的是三个源的鉴权方式还不一样:一个用 Header Token,一个用 Query 参数,一个要签名。@ohos/axios 的多实例 + 拦截器,让每个源的鉴权各管各的,业务代码一行都不用重复。> 📦 仓库地址 | ohpm install @ohos/axios—## 场景"菜谱汇"把下厨房、美食杰、豆果的菜谱聚到一处,用户搜"川菜"能一次看到全网结果。三件事要做:1. 三个源并发请求,合并去重后按评分排序2. 每个源的鉴权方式不同,得隔离管理3. 菜谱封面图下载到本地缓存,要显示进度条用原生 @ohos.net.http 的话,三套鉴权逻辑、三次回调嵌套、三次文件流处理——全是重复代码。—## 为什么是 axios| 需求 | 原生 http | @ohos/axios ||------|-----------|-------------|| 三源并发 | 手写 Promise.all + 回调 | axios.all 一行 || 各源独立鉴权 | 每次手动加 header | 多实例拦截器 || 错误兜底 | 每个请求各写 catch | 响应拦截器统一 || 下载进度 | 手写流式处理 | onDownloadProgress || 某源挂了要容错 | 手写 | Promise.allSettled |—## 关键实现### 每个源一个实例,鉴权互不干扰typescriptimport axios, { AxiosResponse } from '@ohos/axios'interface Recipe { id: string; title: string; cover: string; rating: number }// 源 A:Token 放 Headerconst srcA = axios.create({ baseURL: 'https://api.xiachufang.com', timeout: 10000 })srcA.interceptors.request.use((c) => { c.headers['Authorization'] = 'Bearer ' + tokenA; return c })// 源 B:Token 放 Queryconst srcB = axios.create({ baseURL: 'https://api.meishij.com', timeout: 10000 })srcB.interceptors.request.use((c) => { c.params = { ...c.params, api_key: tokenB }; return c })// 源 C:需要签名const srcC = axios.create({ baseURL: 'https://api.douguo.com', timeout: 10000 })srcC.interceptors.request.use((c) => { c.headers['X-Sign'] = sign(c.url); return c })鉴权逻辑各写一次,之后再怎么加请求都不用管——这是拦截器最省心的地方。### 并发拉取 + 容错某个源挂了不该让整个页面白屏,用 allSettled:typescriptasync function fetchRecipes(category: string): Promise<Recipe[]> { const url = `/recipes?category=${category}` const results = await Promise.allSettled([ srcA.get<Recipe[], AxiosResponse<Recipe[]>, null>(url), srcB.get<Recipe[], AxiosResponse<Recipe[]>, null>(url), srcC.get<Recipe[], AxiosResponse<Recipe[]>, null>(url), ]) const all: Recipe[] = [] results.forEach((r, i) => { if (r.status === 'fulfilled') all.push(...r.value.data) else console.warn(`源 ${i} 失败已跳过: ${r.reason}`) }) // 按评分排序 + 同名去重 const seen = new Set<string>() return all.sort((a, b) => b.rating - a.rating).filter((r) => { if (seen.has(r.title)) return false seen.add(r.title); return true })}### 封面图下载带进度typescriptimport fs from '@ohos.file.fs'async function downloadCover(recipe: Recipe): Promise<string> { const filePath = getContext(this).cacheDir + `/cover_${recipe.id}.jpg` try { fs.accessSync(filePath); return filePath } catch (e) { /* 未缓存,继续下载 */ } const res = await axios.get<ArrayBuffer, AxiosResponse<ArrayBuffer>, null>(recipe.cover, { responseType: 'array_buffer', onDownloadProgress: (e) => { this.percent = e?.total ? Math.ceil(e.loaded / e.total * 100) : 0 }, }) const file = fs.openSync(filePath, fs.OpenMode.CREATE | fs.OpenMode.READ_WRITE) fs.writeSync(file.fd, res.data) fs.closeSync(file) return filePath}—## 一个容易踩的点设了 onDownloadProgress 之后,请求会自动切到流式模式,此时 response.performanceTiming 会是 undefined。如果你既想要进度条又想要性能计时数据,两者拿不到一起——按业务优先级选一个。菜谱列表页要进度条,就别指望同时拿 DNS/TCP 耗时。—## 小结聚合类 App 的痛点从来不是"发请求”,而是多个数据源的差异化处理。@ohos/axios 的多实例拦截器把鉴权、错误、日志这些横切关注点全收敛掉,业务代码只剩下"拉数据、合并、渲染"。再加上 allSettled 的容错和现成的下载进度回调,一个菜谱聚合页半天就能写完。
更多推荐


所有评论(0)