一、前言:为什么项目必须统一封装网络?
绝大多数鸿蒙新手项目的网络写法:每个页面单独 fetch / http 请求。
这种写法上线必出一堆隐性Bug:
商用鸿蒙项目标准:全网统一封装 + 全局拦截器 + 自动Token续期 + 防抖节流 + 自动重试 + 离线缓存。
本篇给企业级可直接上线的完整网络架构,一套代码通用于所有鸿蒙商用、政企、上架项目,可直接写进简历、面试满分、项目直接落地。
二、企业级网络架构分层(标准四层)
商用项目必须分层,禁止所有代码堆在页面。
四层架构(从上到下)
API层:统一管理所有接口地址、请求参数(只定义,不处理逻辑)
Request请求层:统一请求方式、统一参数、统一返回结构
Interceptor拦截层:请求拦截、响应拦截、Token处理、错误统一收口
Cache/Retry工具层:缓存策略、重试策略、防抖、取消请求
优势:改一处、全局生效,维护成本极低,全网行为统一。
三、核心能力清单(本篇全部落地)
四、完整代码实战(可直接商用)
4.1 环境配置与常量定义 config
// config/HttpConfig.etsexport default class HttpConfig { // 正式/测试地址 public static BASE_URL: string = "https://xxx.com/api" // 超时时间 10s public static TIME_OUT: number = 10000 // 重试次数 public static RETRY_COUNT: number = 2 // 重试间隔 public static RETRY_DELAY: number = 800}
4.2 全局Token管理(结合AppStorage)
// store/TokenStore.etsimport AppStorage from '@ohos.appstorage'export class TokenUtil { // 获取Token staticgetToken(): string { return AppStorage.get('token') || '' } // 设置Token staticsetToken(token: string) { AppStorage.setOrCreate('token', token) } // 清除Token staticclearToken() { AppStorage.delete('token') }}
4.3 核心网络拦截器封装(重点)
// utils/HttpRequest.etsimport http from '@ohos.net.http'import promptAction from '@ohos.promptAction'import HttpConfig from '../config/HttpConfig'import { TokenUtil } from '../store/TokenStore'// 存储当前请求队列(用于取消重复请求)const pendingRequest: Set<string> = new Set()// 基础请求封装async function request<T>( url: string, method: http.RequestMethod, data?: Object, header?: Object): Promise<T> { // 1. 请求唯一key防抖 const reqKey = url + JSON.stringify(data) if (pendingRequest.has(reqKey)) { return Promise.reject("重复请求已拦截") } pendingRequest.add(reqKey) // 2. 创建请求实例 const httpRequest = http.createHttp() // 3. 统一请求头 const headers: Object = { 'Content-Type': 'application/json;charset=UTF-8', 'token': TokenUtil.getToken(), ...header } // 4. 请求配置 const option: http.HttpRequestOptions = { method: method, header: headers, readTimeout: HttpConfig.TIME_OUT, connectTimeout: HttpConfig.TIME_OUT, extraData: data } try { // 5. 发起请求 const res = await httpRequest.request(HttpConfig.BASE_URL + url, option) pendingRequest.delete(reqKey) httpRequest.destroy() // 6. 统一解析后端返回格式 const result: any = JSON.parse(res.result.toString()) // 7. 业务状态码拦截 if (result.code === 200) { return result.data } else if (result.code === 401) { // Token过期触发续期 return refreshToken(() => request(url, method, data, header)) } else { promptAction.showToast({ message: result.msg || "请求失败" }) return Promise.reject(result) } } catch (err) { pendingRequest.delete(reqKey) httpRequest.destroy() promptAction.showToast({ message: "网络异常,请检查网络" }) return Promise.reject(err) }}
4.4 高级核心:Token 无感自动续期
企业项目最大痛点:用户操作中途Token过期,直接掉线。
解决方案:401拦截 - 自动刷新Token - 重发失败请求 - 用户无感知。
// 全局锁:防止多次同时刷新let isRefreshing: boolean = falseasync function refreshToken(callback: Function) { if (isRefreshing) return isRefreshing = true try { // 1. 请求刷新Token接口 const res = await request<any>('/user/refresh', http.RequestMethod.POST, { refreshToken: TokenUtil.getToken() }) // 2. 保存新Token TokenUtil.setToken(res.token) // 3. 重发之前失败的接口 return callback() } catch (e) { // 刷新失败 → 清空登录态、跳转登录 TokenUtil.clearToken() promptAction.showToast({ message: "登录已过期,请重新登录" }) } finally { isRefreshing = false }}
4.5 防抖、节流、取消重复请求
上面 pendingRequest 队列已经实现:
短时间相同参数、相同地址请求直接拦截
解决按钮连击、列表多次刷新、重复提交表单
4.6 自动重试机制(弱网适配)
政企设备、工控、弱网环境必备。
async function retryRequest<T>(fn: ()=>Promise<T>, count = 0):Promise<T> { try { return await fn() } catch (e) { if(count < HttpConfig.RETRY_COUNT){ // 延迟重试 await new Promise(res=>setTimeout(res, HttpConfig.RETRY_DELAY)) return retryRequest(fn, count + 1) } return Promise.reject(e) }}
4.7 封装 GET / POST 统一方法
export function get<T>(url: string, data?: Object): Promise<T> { return retryRequest(()=> request(url, http.RequestMethod.GET, data))}export function post<T>(url: string, data?: Object): Promise<T> { return retryRequest(()=> request(url, http.RequestMethod.POST, data))}
4.8 统一API接口管理
// api/IndexApi.etsimport {get, post} from '../utils/HttpRequest'// 首页数据export const getHomeData = (params: any) => get('/home/data', params)// 登录export const loginApi = (params: any) => post('/user/login', params)// 提交表单export const submitFormApi = (params: any) => post('/form/submit', params)
4.9 页面销毁取消请求(防泄漏终极方案)
在页面 aboutToDisappear 清空队列,彻底解决页面销毁接口还在回调的闪退、报错、内存泄漏问题。
aboutToDisappear(){ pendingRequest.clear()}
五、离线缓存策略(商用体验加分)
弱网/无网场景,优先展示旧缓存,网络恢复自动更新。
实现思路:
接口请求成功后,将数据缓存至 Preferences
请求失败/超时,优先读取本地缓存兜底
支持缓存过期时间配置
极大提升政企设备、户外弱网场景用户体验。
六、面试满分答题模板(网络封装必背)
面试官:你项目的网络层是怎么封装的?
标准满分回答:
我在项目中搭建了四层企业级网络架构,统一收口全网请求。通过请求拦截器自动携带Token、统一请求头和超时配置;响应拦截器统一解析返回格式、全局处理报错信息、统一Toast提示。
针对Token过期问题,我实现了401无感自动续期机制,加锁防止并发刷新,续期成功自动重发失败请求,用户全程无感知。
同时做了请求防抖与节流,通过请求队列拦截重复接口,解决按钮连击和重复提交;针对弱网环境实现自动重试策略;页面销毁清空请求队列,彻底杜绝因接口残留导致的内存泄漏和页面报错。并且增加了离线缓存兜底,大幅提升弱网场景体验。
七、简历高薪写法(直接复制)
独立搭建企业级四层网络架构,封装统一请求、拦截器、异常处理;实现Token自动续期、无感刷新登录态;通过请求队列防抖节流解决重复提交问题;适配弱网自动重试与离线缓存兜底;页面生命周期绑定请求销毁,解决网络残留导致的内存泄漏,全网行为统一规范,提升项目稳定性与用户体验。
八、总结
网络层封装是区分初级和中级鸿蒙工程师的核心标杆。只会调接口是初级,会架构、会拦截、会续期、会防抖、会治理网络问题,才是商用高薪标准。