HarmonyOS上做React开发,听起来有点绕,但实际上React Native for OpenHarmony(后面统一叫RNOH)这摊事已经能跑了。我维护的这套开源教程《玩转React》前十七篇都在讲组件、状态管理和网络层,到了第十八篇,正好借“课程详情页面”这个真实场景,把前面的知识点串起来。这篇文章我会把页面拆解、核心模块的实现思路、以及我在真机调试时踩的坑全部写出来,代码逻辑和踩坑记录都是直接能从项目里捞出来复用的。
先交代一下这个页面要解决的问题:用户在首页点开一门课程,进入详情页后需要看到课程封面、标题、简介、课时列表、评价信息,以及底部“立即购买”的固定操作栏。这是典型的电商+内容混合型页面,技术点上涵盖了轮播图、富文本渲染、长列表加载、状态批处理、安全区适配等多个方向。适合正在用RNOH做鸿蒙应用、又想保持React开发习惯的团队参考,也适合刚接触HarmonyOS开发、想找一个完整页面案例的读者。
1. 页面需求拆解与方案选型
1.1 这个页面到底要做什么
课程详情页是知识付费类App里最核心的落地页,用户从任何入口进来,最终都要在这个页面决定“买不买”“学不学”。所以页面不只是展示信息,还要承担转化任务。我拆解功能时列了一个清单,确保开发过程中不遗漏:
- 课程封面区域:多图轮播,支持手势滑动和自动播放;
- 课程基本信息:标题、讲师、价格、学习人数、评分;
- 富文本详情:课程大纲、适合人群、讲师介绍,由后端返回HTML片段;
- 课时列表:可展开折叠,展示每节课的名称、时长、试听标识;
- 评分与评论:只展示前几条热门评论,支持跳转到全部评论页;
- 底部操作栏:收藏+购买/试听按钮,购买按钮随时根据课程状态变化。
这个页面在React Native for OpenHarmony上实现时,有几个地方跟原生ArkTS页面思路不一样。RNOH里没有“Ability切片”这种概念,页面就是React组件,路由用react-navigation管理,页面之间通过参数传递课程ID。这跟Web端React开发的心智模型几乎一样,团队上手成本低很多。
1.2 为什么不用ArkTS原生,而是选React
很多做鸿蒙开发的朋友看到“React”就皱眉,觉得HarmonyOS就应该用ArkTS写。我不否认ArkTS是鸿蒙一等公民,官方生态也最全。但现实情况是,很多团队手里已经有一份React Native的代码库,或者团队主要技术栈是React,这时候再为鸿蒙单独维护一套ArkTS代码,成本很高。RNOH的价值在于复用,它能把现有的React Native业务代码跑到鸿蒙设备上,补齐iOS和Android之外的第三端。
技术选型上,我建议按这个标准判断:
- 如果是从零启动、只做鸿蒙的单端应用,直接学ArkTS更省事,没必要绕一圈React;
- 如果已经有多端React Native代码,或者团队React经验明显强于ArkTS,RNOH是降低维护成本的好方案;
- 如果App对硬件能力(蓝牙、NFC、传感器)有强依赖,现阶段ArkTS的API覆盖更完整,React那边还得等桥接生态慢慢补。
我们这个教程系列定位是“玩转React”,就是假设你熟悉React但不太熟鸿蒙,所以全程用RNOH思路来讲。课程详情页用到的轮播图、富文本、列表组件,RNOH社区都已经有可用的库,真正要花时间处理的是鸿蒙端特有的样式兼容和性能细节,这些我后面会重点展开。
1.3 技术栈和依赖版本
我在这个教程系列里统一固定了一套版本组合,避免大家在依赖版本上踩坑:
| 依赖 | 版本 | 说明 |
|---|---|---|
| react | 18.2.0 | 使用并发特性和批处理机制 |
| react-native | 0.72.x | RNOH基于这个版本适配 |
| @react-navigation/native | 6.x | 页面导航 |
| react-native-swiper | 1.6.x | 轮播图组件 |
| react-native-render-html | 6.x | 富文本渲染 |
| @react-native-community/slider | 4.x | 底部操作栏相关滑动需求 |
RNOH的版本对应关系比较固定,0.72版本对应OpenHarmony 4.x。开发时我直接参考官方仓库的sample工程初始化项目,比自己在空工程里一个个装依赖稳得多。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 课程详情页的整体架构设计
2.1 页面结构拆成组件树
页面本身就是一个大组件,但如果把全部逻辑堆在一个文件里,后期没法维护。我按功能域拆成了六个子组件,每个组件只负责一块独立逻辑:
code复制CourseDetailScreen
├── CourseSwiper # 封面轮播
├── CourseInfo # 标题、讲师、价格、学习人数
├── CourseRichContent # 富文本详情
├── CourseChapterList # 课时列表(可折叠)
├── CourseComments # 热门评论
└── BottomActionBar # 收藏 + 购买按钮
拆分的核心标准是“独立状态”。比如轮播图有当前索引状态,ChapterList有展开收起状态,BottomActionBar有收藏状态的本地缓存,这些状态彼此不相关,拆开以后各自维护,父组件不需要关心它们内部怎么变化。父组件只负责拉取课程数据,然后通过props分发下去。
组件树设计的一个细节是数据形态。后端返回的课程详情是个嵌套结构,比如course对象里有chapters数组,chapters里又包含lessons数组。如果直接把原始对象传给子组件,子组件访问路径会很长而且容易出错。我在实战中会在父组件里把数据重新整理成扁平结构,比如把课时列表单独抽出来,让ChapterList只接收lessonList数组,它的props就清晰多了。
2.2 数据模型与网络层设计
课程详情接口返回的数
