【开源鸿蒙跨平台开发学习笔记】Day07:React Native 开发 HarmonyOS-GitCode口袋工具开发-3
本文详细介绍了基于ReactNative和HarmonyOS开发GitCode口袋工具的过程。文章重点阐述了网络层封装(使用Axios拦截器处理Token注入和错误统一)、RepoItem仓库卡片组件设计、首页FlatList高性能渲染等关键技术实现。项目采用ReactNative0.72与@react-native-oh/react-native-harmony环境,完整覆盖了从开发调试到离线包
前言
本次参加开源鸿蒙跨平台开发学习活动,选择了 React Native 开发 HarmonyOS技术栈,在学习的同时顺便整理成一份系列笔记,记录从环境到开发的全过程。本篇作为第七篇,在前几篇内容中,我们已经完成了 React Native 在 HarmonyOS 端的基础环境搭建与底部 TabBar 的构建。本篇将继续推进 GitCode 口袋工具的开发,重点整理 仓库列表展示(Star 列表)、网络层封装、仓库卡片组件 RepoItem、首页渲染与路由组织结构。
本篇内容完全基于 React Native 0.72 与 @react-native-oh/react-native-harmony 运行环境,代码均可直接在 HarmonyOS NEXT 中运行。
一、项目背景与目标
为了更好地验证 HarmonyOS 上的 React Native 生态与体验,我制作了一个 GitCode 口袋工具 Demo:
-
首页:展示 GitCode 用户信息与“已 Star 仓库列表”
-
自研 TabBar,纯手写 Navigation 与路由结构
-
重点:
✔ 统一网络层(axios 拦截器、错误处理)
✔ RepoItem 仓库卡片组件复用
✔ FlatList 合理渲染长列表
✔ HarmonyOS 端 Metro 联调 + 离线包打包流程
开发环境依赖如下:
react-native@0.72.5
react@18.2.0
@react-native-oh/react-native-harmony@^0.72.90
Metro Bundler 需要额外合并 Harmony 特殊配置,这部分前文已处理,本篇不再重复。
二、统一网络层设计(Axios 拦截器)
移动应用的网络请求往往涉及 Token 注入、错误提示规范化等逻辑,没有统一管理很容易导致代码分散。本项目构建了一个 http 实例,集中处理:
-
基础域名 baseURL
-
超时时间
-
注入 private-token
-
错误统一文案格式化
代码如下:
import axios from 'axios';
const DEFAULT_TOKEN = 'YOUR_PRIVATE_TOKEN';
let privateToken = DEFAULT_TOKEN;
export function setPrivateToken(token?: string) {
if (token) privateToken = token;
}
export const http = axios.create({
baseURL: 'https://api.gitcode.com/api/v5/',
timeout: 10000,
});
http.interceptors.request.use(config => {
const headers = config.headers ?? {};
headers['private-token'] = privateToken;
config.headers = headers;
return config;
});
http.interceptors.response.use(res => res, err => Promise.reject(err));
export function getErrorMessage(error: unknown): string {
if (axios.isAxiosError(error)) {
const status = error.response?.status;
const data = error.response?.data as any;
const msg = typeof data === 'string' ? data : data?.message || data?.error || error.message;
return status ? `${status} ${msg}` : msg;
}
return String((error as any)?.message || error);
}
页面中只需关注 拿数据,无需关心 Token、错误处理等杂项,大幅减少模板代码。
三、数据类型与 API 封装
GitCode API 的仓库、用户结构较大,项目中只选取需要字段:
export type RepoOwner = {
id?: string | number;
login?: string;
name?: string;
avatar_url?: string;
html_url?: string;
type?: string;
};
export type Repo = {
id: string | number;
name?: string;
path?: string;
full_name?: string;
description?: string;
language?: string;
stargazers_count?: number;
watchers_count?: number;
commits_count?: number;
web_url?: string;
html_url?: string;
owner?: RepoOwner;
};
API 请求:
import {http} from './client';
import {UserProfile} from '../types/user';
import {Repo} from '../types/repo';
const PROFILE_PATH = 'users/qiaomu8559968';
export async function fetchUserProfile(): Promise<UserProfile> {
const res = await http.get<UserProfile>(PROFILE_PATH);
return res.data;
}
export async function fetchStarred(username: string): Promise<Repo[]> {
const res = await http.get<Repo[]>(`users/${username}/starred`, {
params: {access_token: 'YOUR_ACCESS_TOKEN'},
});
return res.data;
}
四、仓库卡片组件 RepoItem 封装
GitCode 的仓库卡片要展示:Logo、名称、Star 按钮、描述、语言、Stars、Commits。
我们保持组件结构干净、参数明确,方便后续复用。
export type RepoItemProps = {
logo: string;
name: string;
description: string;
language: string;
stars: number;
commits: number;
isStarred: boolean;
onToggleStar: () => void;
};
核心渲染内容如下(省略样式):
<View style={styles.container}>
<Image source={{uri: logo}} style={styles.logo} />
<View style={styles.content}>
<View style={styles.row}>
<Text style={styles.name}>{name}</Text>
<TouchableOpacity onPress={onToggleStar} style={styles.starBtn}>
<Text style={styles.starText}>{isStarred ? '★ Starred' : '☆ Star'}</Text>
</TouchableOpacity>
</View>
{!!description && <Text style={styles.desc}>{description}</Text>}
<View style={styles.footer}>
{!!language && <Text style={styles.lang}>{language}</Text>}
<Text style={styles.meta}>⭐ {stars}</Text>
<Text style={styles.meta}>🔗 {commits}</Text>
</View>
</View>
</View>
注意:仓库卡片的根容器必须设置 width: '100%',否则 FlatList 会出现空白间距。
五、首页整合:并发加载 + FlatList 渲染
为了避免 ScrollView 嵌套列表导致的虚拟化错误,本项目采用 FlatList 作为页面根容器,顶部用户信息用 ListHeaderComponent 渲染。
示例代码:
useEffect(() => {
let mounted = true;
setLoading(true);
Promise.all([fetchUserProfile(), fetchStarred('qiaomu8559968')])
.then(([d, s]) => {
if (!mounted) return;
setUser(d);
setStarred((s || []).map(r => ({...r, isStarred: true})));
})
.catch(e => setError(getErrorMessage(e)))
.finally(() => setLoading(false));
return () => { mounted = false; };
}, []);
FlatList:
<FlatList
data={starred}
keyExtractor={(item, index) => String(item.id ?? index)}
renderItem={({item}) => (
<RepoItem
logo={item.owner?.avatar_url || 'https://cdn-img.gitcode.com/logo.png'}
name={item.name || item.path || item.full_name || '未知仓库'}
description={item.description || ''}
language={item.language || ''}
stars={item.stargazers_count || 0}
commits={item.commits_count || item.watchers_count || 0}
isStarred={!!item.isStarred}
onToggleStar={() => {
setStarred(prev =>
prev.map(r =>
(r.id ?? r.name) === (item.id ?? item.name)
? {...r, isStarred: !r.isStarred}
: r,
),
);
}}
/>
)}
ListHeaderComponent={<UserInfoHeader data={user} />}
contentContainerStyle={styles.listContent}
/>
六、HarmonyOS 调试与打包关键点
1. 开发调试(Metro)
npm run start
hdc rport tcp:8081 tcp:8081
设备上点击 Reload 拉取 bundle:
http://localhost:8081/index.bundle?platform=harmony
2. 打离线包
npx react-native bundle-harmony --dev false
编译产物:
harmony/entry/src/main/resources/rawfile/bundle.harmony.js
确保文件正确包含,或推送:
/data/storage/el2/base/files/bundle.harmony.js
七、常见问题与解决方案
1. ScrollView 嵌套 FlatList 触发虚拟化错误

解决:整个页面使用 FlatList,顶部内容用 ListHeaderComponent
2. 列表项宽度撑不满
解决:
-
RepoItem 设置
width: '100%' -
FlatList 设置合适的
contentContainerStyle
3. Token 安全
解决:不要硬编码 Token,正式环境使用安全注入或环境变量。
最后实现效果:

八、可扩展方向
-
分页加载 / 下拉刷新
-
Skeleton 骨架屏提升加载体验
-
封装 useRequest 统一管理加载、错误、刷新逻辑
-
抽象 RepoItem 为更通用的列表项组件
总结
本篇主要完成了 GitCode 口袋工具的核心内容:
- 网络层统一封装
- RepoItem 组件化设计
- FlatList 高性能渲染
- HarmonyOS Metro 调试与离线包流程
随着仓库列表结构已搭建完毕,下一篇将继续围绕导航体系、更多页面拆分与项目结构优化展开,为后续的功能扩展打下基础。
更多推荐




所有评论(0)