React Native鸿蒙跨平台使用了 ScrollView 实现分类标签的水平滚动和新闻列表的垂直滚动,确保滚动的流畅性
欢迎大家加入开源鸿蒙跨平台开发者社区,一起共建开源鸿蒙跨平台生态。
在移动应用开发领域,新闻资讯类应用因其信息密度高、交互频繁的特点,成为检验框架能力的重要场景。本文将深入解读一个基于 React Native 开发的新闻资讯应用代码片段,剖析其架构设计、组件化策略以及在鸿蒙系统上的跨端实现考量。
代码采用了清晰的组件化设计,将UI拆分为三个核心组件:NewsCard、SearchBar 和 CategoryTab。这种拆分不仅提高了代码的可维护性,更重要的是为跨端开发提供了便利。在鸿蒙系统的 ArkTS 环境中,组件化设计使得平台特定的代码修改可以被隔离在最小范围内,降低了跨端开发的复杂度。
// 新闻卡片组件
const NewsCard = ({
title,
summary,
author,
time,
category,
readTime,
image,
comments,
likes,
isBookmarked
}: {
title: string;
summary: string;
author: string;
time: string;
category: string;
readTime: string;
image: string;
comments: number;
likes: number;
isBookmarked: boolean;
}) => {
// 组件内部逻辑
};
NewsCard 组件的设计尤为值得关注,它通过 props 接收所有必要的数据,并使用内部状态管理收藏状态。这种设计模式使得组件既可以响应外部数据变化,又能管理自身的交互状态,非常适合新闻卡片这种需要独立交互的场景。
状态管理
应用采用了 React Hooks 中的 useState 进行状态管理,这是 React Native 开发中的标准实践。在 NewsCard 组件中,使用 useState 管理收藏状态:
const [bookmarked, setBookmarked] = useState(isBookmarked);
const handleBookmark = () => {
setBookmarked(!bookmarked);
Alert.alert('提示', `已${bookmarked ? '取消收藏' : '收藏'}此文章`);
};
在主组件 NewsPage 中,使用 useState 管理当前选中的分类:
const [selectedCategory, setSelectedCategory] = useState(0);
这种状态管理策略在跨端场景下表现出色,因为它不依赖于特定平台的状态管理方案,而是使用了 React 生态的标准 API,确保了在 React Native 和鸿蒙系统上的一致性表现。
响应式
代码通过 Dimensions.get('window') 获取屏幕宽度,为后续的响应式布局提供了基础:
const { width } = Dimensions.get('window');
这种方式在 React Native 中非常常见,但在鸿蒙系统的跨端开发中,需要特别注意不同设备尺寸的适配。鸿蒙系统的自适应布局能力虽然强大,但与 React Native 的布局模型存在差异,因此在实际开发中需要进行针对性调整。
应用使用了 React Native 内置的 StyleSheet 进行样式管理,这是一种性能优化的最佳实践。通过 StyleSheet.create 创建的样式对象,会被 React Native 转换为原生样式,提高渲染性能。
在鸿蒙系统的跨端开发中,样式系统的转换是一个关键点。鸿蒙的 ArkTS 样式系统与 React Native 的 StyleSheet 存在语法差异,因此需要通过跨端框架(如 HarmonyOS React Native)进行转换。这种转换过程中的性能损耗和样式一致性,是开发中需要重点关注的问题。
滚动视图
代码使用了 ScrollView 实现分类标签的水平滚动和新闻列表的垂直滚动:
<ScrollView
horizontal
showsHorizontalScrollIndicator={false}
style={styles.categoryScrollView}
contentContainerStyle={styles.categoryScrollContent}
>
{/* 分类标签 */}
</ScrollView>
在鸿蒙系统上,ScrollView 的滚动性能需要特别关注。由于鸿蒙系统对原生组件的渲染速度较快,但对 JavaScript 执行的性能要求较高,因此应尽量减少 ScrollView 中的复杂计算,确保滚动的流畅性。
事件处理机制
应用实现了丰富的交互功能,如新闻收藏、点赞、评论等。在 NewsCard 组件中,收藏功能的实现如下:
const handleBookmark = () => {
setBookmarked(!bookmarked);
Alert.alert('提示', `已${bookmarked ? '取消收藏' : '收藏'}此文章`);
};
这种基于回调函数的事件处理方式,在 React Native 和鸿蒙系统中都能很好地工作。但需要注意的是,在鸿蒙系统中,事件响应的优先级和处理机制可能与 React Native 有所不同,需要进行适当的调整以确保用户体验的一致性。
代码中采用了以下性能优化策略:
-
组件拆分:将UI拆分为多个可复用组件,减少单个组件的复杂度,提高渲染性能。
-
合理使用 ScrollView:对于分类标签使用水平 ScrollView,对于新闻列表使用垂直 ScrollView,避免一次性渲染过多内容。
-
条件渲染:通过
isBookmarked状态控制收藏图标的显示,避免不必要的渲染。 -
样式复用:使用 StyleSheet 创建样式对象,减少运行时的样式计算。
在鸿蒙系统上,这些优化策略同样适用,但需要根据鸿蒙系统的性能特点进行适当调整。例如,鸿蒙系统对原生组件的渲染速度较快,但对 JavaScript 执行的性能要求较高,因此应尽量减少 JavaScript 线程的计算负担。
组件
React Native 的核心组件(如 View、Text、Image、TouchableOpacity 等)在鸿蒙系统上都有对应的实现,但在某些属性和行为上可能存在差异。例如,Image 组件的加载策略、TouchableOpacity 的点击反馈等,都需要在鸿蒙系统上进行测试和优化。
图片加载
代码中使用了网络图片加载:
<Image source={{ uri: image }} style={styles.newsImage} />
在鸿蒙系统上,图片加载的性能和缓存策略可能与 React Native 有所不同。为了确保良好的用户体验,应考虑以下几点:
-
图片缓存:实现图片的本地缓存,减少网络请求次数。
-
图片压缩:根据设备屏幕尺寸,加载合适大小的图片,减少内存消耗。
-
懒加载:实现图片的懒加载,提高页面加载速度。
在鸿蒙系统上,React Native 应用的性能优化需要考虑以下几点:
-
渲染性能:鸿蒙系统对原生组件的渲染速度较快,但对 JavaScript 执行的性能要求较高。因此,应尽量减少 JavaScript 线程的计算负担,将复杂的计算逻辑移至原生层。
-
内存管理:新闻应用通常会加载大量图片,内存消耗较大。在鸿蒙系统上,需要特别注意内存的分配和释放,避免内存泄漏。
-
网络请求:新闻应用的网络请求频繁,应合理使用缓存策略,减少网络请求次数,提高应用响应速度。
类型安全
代码使用了 TypeScript 进行类型定义,这不仅提高了代码的可读性,也为跨端开发提供了类型安全保障。在鸿蒙系统的 ArkTS 环境中,类型系统的一致性尤为重要,它确保了数据在不同平台间传递时的准确性。
模块化
应用的代码结构清晰,将数据、组件逻辑、样式等分离,便于维护和扩展。这种模块化设计在跨端开发中尤为重要,因为它使得平台特定的代码修改可以被隔离在最小范围内。
错误处理
代码中使用了 Alert.alert 进行用户反馈,这是一种简单有效的错误处理方式。在实际开发中,还应考虑添加更全面的错误处理机制,如网络请求错误、图片加载失败等,以提高应用的稳定性。
代码展示了组件化开发的最佳实践:
-
单一职责原则:每个组件只负责一个特定的功能,如
NewsCard只负责新闻卡片的展示和交互。 -
props 传递:通过 props 传递数据,使得组件之间的通信清晰明了。
-
内部状态管理:对于组件内部的状态(如收藏状态),使用
useState进行管理,避免状态提升带来的复杂性。
响应式设计
代码通过 Dimensions.get('window') 获取屏幕宽度,为响应式布局提供了基础。在实际开发中,还可以考虑使用 Flexbox 布局模型,进一步提高应用的响应能力。
-
使用核心组件:优先使用 React Native 的核心组件,这些组件在鸿蒙系统上有较好的兼容性。
-
避免平台特定代码:尽量避免使用平台特定的 API,如需要使用,应通过条件判断进行隔离。
-
性能优化:针对不同平台的性能特点,进行有针对性的优化。
-
用户体验:确保在不同平台上的用户体验一致性,包括交互方式、视觉效果等。
通过对这个 React Native 新闻资讯应用代码片段的深入解读,我们可以看到,一个优秀的跨端应用需要在架构设计、组件化策略、性能优化等多个方面进行精心考量。特别是在 React Native 与鸿蒙系统的跨端开发中,需要充分了解两个平台的特性,才能开发出性能优异、用户体验一致的应用。
随着移动应用开发技术的不断演进,跨端开发已成为一种趋势。React Native 作为一种成熟的跨端开发框架,在鸿蒙系统上的应用前景广阔。通过不断优化代码结构、提高性能表现、确保用户体验一致性,我们可以开发出更加优秀的跨端应用,为用户带来更好的使用体验。
在未来的开发中,我们还可以探索更多技术方案,如使用 Redux 或 MobX 进行状态管理、使用 React Navigation 进行路由管理、使用 Expo 进行快速开发等,进一步提高开发效率和应用质量。同时,也需要密切关注鸿蒙系统的发展动态,及时调整开发策略,以适应新的技术要求。
新闻资讯类应用是内容消费场景的典型代表,其包含的组件化拆分、流式列表渲染、分类标签交互、状态驱动UI变化等核心能力,是跨端开发中高频且关键的技术场景。本文以一个完整的 React Native 新闻资讯页面为例,拆解其工程化实现逻辑,并深入探讨该页面向鸿蒙(HarmonyOS)生态迁移的技术要点,为跨端内容类应用开发提供可落地的实践参考。
该新闻资讯页面采用 React Native 主流的组件化开发范式,融合了纯函数组件设计、Props 类型约束、多层级 Flex 布局、局部状态管理等核心技术点,完全贴合内容类应用的开发特性。
1. 组件化设计:
页面采用精细化的组件拆分策略,将 UI 拆分为 NewsCard、SearchBar、CategoryTab 三个独立组件 + 主页面容器,这种设计是跨端复用的核心基础:
- 原子级组件封装:
SearchBar封装搜索栏UI,CategoryTab封装分类标签的样式与交互逻辑,NewsCard封装单条新闻的完整展示与交互(点赞、评论、收藏)。每个组件仅关注自身职责,通过 Props 接收外部数据,这种“单一职责”的组件设计在鸿蒙端可直接映射为自定义组件(@Component装饰的 struct),实现组件级别的跨端复用。 - Props 类型强约束:
NewsCard组件通过 TypeScript 明确定义入参类型(如title: string、isBookmarked: boolean),避免运行时类型错误,这种类型约束在鸿蒙 ArkTS 中可通过接口(interface)无缝复用,仅需将 React 的 Props 传递方式改为鸿蒙的参数传递,核心类型定义完全一致。 - 组件内部状态自治:
NewsCard内部通过useState管理收藏状态(bookmarked),收藏交互逻辑(handleBookmark)也内聚在组件内部,这种“状态自治”的设计让组件具备独立复用能力,迁移至鸿蒙时仅需将useState替换为@State,交互逻辑无需修改。
2. 布局体系:
新闻页面的布局完全基于 React Native 的 Flex 布局体系实现,而 Flex 是 React Native 与鸿蒙 ArkUI 共有的核心布局模型,为跨端迁移提供了天然优势:
- 响应式容器设计:通过
Dimensions.get('window')获取屏幕宽度,结合flex: 1、width: '100%'等属性实现页面容器的自适应,新闻图片设置height: 180保证固定视觉高度,这种布局策略在鸿蒙端可通过screen.getScreenSize()获取屏幕尺寸后,结合width: '100%'、height: 180vp实现等价适配,无需为不同设备编写差异化布局。 - 多层级 Flex 嵌套:
newsCard内部通过垂直 Flex 布局实现图片+内容的上下排列;newsHeader借助justifyContent: 'space-between'实现分类标签与阅读时长的两端对齐;newsFooter通过 Flex 实现作者信息与操作按钮的左右分栏。整个布局逻辑完全基于 Flex 实现,迁移至鸿蒙时仅需调整属性命名规范(如flexDirection: 'row'→flexDirection: FlexDirection.Row),核心逻辑无需重构。 - 视觉层次构建:通过
elevation(Android 阴影)、borderRadius、overflow: 'hidden'等样式属性构建新闻卡片的视觉层次感,分类标签通过backgroundColor: '#dbeafe'+borderRadius: 12实现胶囊样式,这种视觉交互的实现方式在鸿蒙端可通过shadow、borderRadius、backgroundColor属性无缝替换,保证跨端视觉体验一致。
3. 列表与滚动:
新闻页面包含横向滚动的分类标签和纵向滚动的新闻列表,两种滚动场景的实现体现了 React Native 滚动交互的工程化思路,也为跨端迁移提供了清晰的映射方案:
- 横向分类标签:采用
ScrollView horizontal实现横向滚动,配置showsHorizontalScrollIndicator={false}隐藏滚动指示器优化体验,contentContainerStyle配置内边距保证交互合理性。这种横向滚动布局在鸿蒙端可通过Scroll组件 +FlexDirection.Row实现,核心属性(如隐藏滚动条、内容容器样式)均可等价映射。 - 纵向新闻列表:采用基础
ScrollView渲染新闻卡片列表(数据量较小场景),若后续数据量增大,可无缝切换为FlatList实现虚拟化列表,这种“按需选择滚动组件”的思路在跨端开发中同样适用——少量静态数据用ScrollView,大量动态数据用虚拟化列表(React Native 的FlatList/ 鸿蒙的List)。
4. 状态与交互:
页面采用 React 内置的 useState 管理局部状态,结合 TouchableOpacity 实现交互逻辑,符合内容类应用的轻量状态管理最佳实践:
- 主页面通过
useState管理选中的分类标签(selectedCategory),点击分类标签时更新状态并触发 UI 重渲染,这种状态驱动 UI 变化的逻辑在鸿蒙端可通过@State装饰器实现,状态更新逻辑(setSelectedCategory(index)→this.selectedCategory = index)完全一致。 NewsCard组件内部的收藏交互、点赞/评论的弹窗反馈(Alert.alert),均为纯业务逻辑,与 UI 框架解耦,迁移至鸿蒙时仅需将Alert.alert替换为promptAction.showDialog,核心交互逻辑无需修改。
将该新闻页面迁移至鸿蒙端,核心是“组件复用、布局等价、交互一致”,以下从技术维度拆解关键适配点:
1. 语法适配:
鸿蒙端基于 ArkTS(TypeScript 超集)开发,与 React Native 的 TypeScript 语法高度兼容,核心差异集中在组件定义与状态管理:
- 组件定义:React Native 的函数式组件(如
const NewsCard = ({ ...props }) => {})对应鸿蒙的自定义组件(@Component struct NewsCard {}),React 的 Props 入参对应鸿蒙的组件参数,例如NewsCard组件的入参类型可直接复用,仅需将函数返回 JSX 的方式改为鸿蒙的 build 方法返回 UI 结构。 - 状态管理:React 的
useState对应鸿蒙的@State装饰器,例如const [bookmarked, setBookmarked] = useState(isBookmarked)可改写为@State bookmarked: boolean = isBookmarked,状态更新逻辑从setBookmarked(!bookmarked)改为this.bookmarked = !this.bookmarked,逻辑完全一致。 - 事件处理:React Native 的
onPress对应鸿蒙的onClick,事件绑定方式从onPress={handleBookmark}改为onClick={() => this.handleBookmark()},事件处理函数的内部逻辑无需修改。
2. 核心组件:
React Native 原生组件与鸿蒙 ArkUI 组件存在清晰的映射关系,是跨端迁移的核心落地环节:
| React Native 组件 | 鸿蒙 ArkUI 组件 | 适配核心说明 |
|---|---|---|
| SafeAreaView | SafeArea | 均用于适配刘海屏/底部安全区,属性一致 |
| View | Column/Row/Stack | 鸿蒙通过布局组件替代通用容器,Flex 布局逻辑复用 |
| Text | Text | 样式属性(fontSize/color等)仅命名规范差异 |
| TouchableOpacity | Button/Text(带点击态) | 鸿蒙无直接等价组件,可通过 Button 去除默认样式实现,或自定义 Text 的点击态 |
| ScrollView (horizontal) | Scroll + Row | 鸿蒙 Scroll 组件结合 FlexDirection.Row 实现横向滚动 |
| Image | Image | 图片加载方式从 source={{ uri: image }} 改为 src: image,resizeMode 对应 objectFit |
| Alert | promptAction | 鸿蒙 promptAction.showDialog/showToast 实现弹窗/提示,参数结构需适配 |
以核心的 NewsCard 组件为例,React Native 实现与鸿蒙 ArkTS 实现的核心映射:
// React Native NewsCard 核心逻辑
const NewsCard = ({ isBookmarked, ...props }) => {
const [bookmarked, setBookmarked] = useState(isBookmarked);
const handleBookmark = () => {
setBookmarked(!bookmarked);
Alert.alert('提示', `已${bookmarked ? '取消收藏' : '收藏'}此文章`);
};
// JSX 渲染逻辑...
};
// 鸿蒙 ArkTS NewsCard 等价实现
@Component
struct NewsCard {
@Prop isBookmarked: boolean;
@State bookmarked: boolean = this.isBookmarked;
// 其他 Props 定义...
handleBookmark() {
this.bookmarked = !this.bookmarked;
promptAction.showToast({
message: `已${this.bookmarked ? '取消收藏' : '收藏'}此文章`
});
}
build() {
// ArkUI 渲染逻辑(复用 Flex 布局)...
}
}
React Native 的 StyleSheet.create 封装样式的方式,在鸿蒙端可通过 @Styles/@Extend 装饰器实现等价封装,核心样式属性的适配规则如下:
- 布局属性:
flex/flexDirection/justifyContent/alignItems等 Flex 核心属性完全复用,仅鸿蒙需将字符串值改为枚举值(如justifyContent: 'space-between'→justifyContent: FlexAlign.SpaceBetween)。 - 尺寸与间距:React Native 的
padding: 16对应鸿蒙的padding: 16vp,width: '100%'对应width: '100%',height: 180对应height: 180vp,单位适配无成本。 - 视觉样式:
borderRadius完全复用;elevation(Android 阴影)对应鸿蒙的shadow属性(shadowColor/shadowRadius/shadowOffset);overflow: 'hidden'对应鸿蒙的overflow: Overflow.Hidden,视觉效果完全一致。 - 条件样式:React Native 的
[styles.categoryTab, isSelected && styles.selectedCategoryTab]条件样式,在鸿蒙端可通过三元表达式实现:style={this.isSelected ? this.selectedCategoryTab() : this.categoryTab()},样式切换逻辑完全复用。
新闻资讯类应用对滚动流畅度、图片加载体验要求较高,跨端迁移需重点保证两端体验的一致性:
- 滚动性能:React Native 的
ScrollView和鸿蒙的Scroll组件均为原生滚动实现,鸿蒙端可通过cachedCount配置滚动缓存项数量,优化大量新闻数据的滚动流畅度;若后续切换为虚拟化列表(FlatList/List),需保证鸿蒙List开启虚拟化能力(默认开启)。 - 图片加载:React Native 的
Image组件与鸿蒙的Image组件均支持网络图片加载,鸿蒙端可结合@ohos.image模块实现图片懒加载、缓存等优化,提升新闻图片的加载体验。 - 交互反馈:React Native 的
TouchableOpacity点击透明度变化反馈,在鸿蒙端可通过Button组件的stateEffect属性实现,或自定义Text组件的backgroundColor点击态切换,保证分类标签、新闻操作按钮的交互反馈一致性。 - 分布式适配:鸿蒙特有的分布式能力可作为跨端增强点,例如该新闻页面可通过鸿蒙的
@ohos.distributedData实现多设备收藏同步,或通过@ohos.window适配平板/智慧屏的大屏布局(如新闻卡片双列展示、分类标签横向平铺),提升鸿蒙端的原生体验。
从该 React Native 新闻页面的鸿蒙适配过程中,可提炼出内容类应用跨端开发的通用方法论:
1. 组件化拆分
该页面将 UI 拆分为 NewsCard、SearchBar、CategoryTab 等独立组件,每个组件职责单一、状态自治,这种设计让跨端迁移可实现“组件级复用”,而非整体代码移植,大幅降低适配成本。在跨端开发初期,应优先完成组件化拆分,明确组件的 Props 类型和内部状态,为后续跨端适配奠定基础。
2. Flex 布局
React Native 与鸿蒙 ArkUI 均基于 Flex 布局体系,这是跨端布局适配的最大优势。在开发时应优先使用 Flex 布局而非固定尺寸布局,减少不同设备、不同平台的适配工作量,核心布局逻辑可 100% 复用。
3. 状态与逻辑层
页面的业务逻辑(如收藏状态切换、分类选中逻辑)与 UI 渲染解耦,仅通过状态驱动 UI 变化,这种设计让核心业务逻辑可完全跨端复用,仅需适配 UI 层的组件和样式,是跨端开发效率最大化的关键。
若将该新闻页面落地到鸿蒙端,建议分阶段实施:
- 组件迁移:首先迁移
SearchBar、CategoryTab等简单组件,验证组件定义、Props 传递、样式适配的可行性,再迁移核心的NewsCard组件。 - 逻辑复用:直接复用 TypeScript 类型定义和业务逻辑函数(如收藏状态切换、分类选中逻辑),仅修改状态管理和弹窗反馈的 API 调用方式。
- 体验优化:针对鸿蒙端特性做体验增强,例如利用鸿蒙的
List组件实现新闻列表的下拉刷新/上拉加载,利用Image组件的加载状态优化新闻图片的展示体验。
从性能优化角度,React Native 端可将新闻列表从 ScrollView 改为 FlatList 实现虚拟化渲染,鸿蒙端可通过 List 组件的 lazyLoad 特性优化大量新闻数据的加载性能。
该 React Native 新闻资讯页面的实现,充分体现了现代跨端开发的工程化思想:组件化拆分提升复用性,Flex 布局实现多端适配,TypeScript 保证类型安全,轻量状态管理降低复杂度。向鸿蒙端迁移时,核心业务逻辑和布局逻辑可完全复用,仅需适配组件语法、样式属性和原生 API 调用,适配成本可控制在 20%-30% 以内。
对于内容类跨端应用开发,无需追求“一行代码多端运行”,而是通过“组件复用 + 布局等价 + 逻辑解耦”的方式,平衡开发效率与原生体验。React Native 与鸿蒙 ArkTS 均基于 TypeScript 生态,Flex 布局体系一致,这为跨端开发提供了天然的技术基础,也是未来移动跨端开发的主流方向。
真实演示案例代码:
// app.tsx
import React, { useState } from 'react';
import { SafeAreaView, View, Text, StyleSheet, TouchableOpacity, ScrollView, Image, Dimensions, Alert } from 'react-native';
// 图标库
const ICONS = {
home: '🏠',
news: '📰',
like: '👍',
comment: '💬',
share: '📤',
bookmark: '🔖',
user: '👤',
search: '🔍',
};
const { width } = Dimensions.get('window');
// 新闻卡片组件
const NewsCard = ({
title,
summary,
author,
time,
category,
readTime,
image,
comments,
likes,
isBookmarked
}: {
title: string;
summary: string;
author: string;
time: string;
category: string;
readTime: string;
image: string;
comments: number;
likes: number;
isBookmarked: boolean;
}) => {
const [bookmarked, setBookmarked] = useState(isBookmarked);
const handleBookmark = () => {
setBookmarked(!bookmarked);
Alert.alert('提示', `已${bookmarked ? '取消收藏' : '收藏'}此文章`);
};
return (
<View style={styles.newsCard}>
<Image source={{ uri: image }} style={styles.newsImage} />
<View style={styles.newsContent}>
<View style={styles.newsHeader}>
<Text style={styles.categoryTag}>{category}</Text>
<Text style={styles.readTime}>{readTime}</Text>
</View>
<Text style={styles.newsTitle}>{title}</Text>
<Text style={styles.newsSummary}>{summary}</Text>
<View style={styles.newsFooter}>
<View style={styles.authorInfo}>
<Text style={styles.author}>{author}</Text>
<Text style={styles.time}>{time}</Text>
</View>
<View style={styles.newsActions}>
<TouchableOpacity style={styles.actionButton} onPress={() => Alert.alert('点赞')}>
<Text style={styles.actionText}>{ICONS.like} {likes}</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.actionButton} onPress={() => Alert.alert('评论')}>
<Text style={styles.actionText}>{ICONS.comment} {comments}</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.actionButton} onPress={handleBookmark}>
<Text style={styles.actionText}>{bookmarked ? '★' : '☆'}</Text>
</TouchableOpacity>
</View>
</View>
</View>
</View>
);
};
// 搜索栏组件
const SearchBar = () => {
return (
<View style={styles.searchBar}>
<Text style={styles.searchIcon}>{ICONS.search}</Text>
<Text style={styles.searchPlaceholder}>搜索新闻...</Text>
</View>
);
};
// 分类标签组件
const CategoryTab = ({
title,
isSelected,
onPress
}: {
title: string;
isSelected: boolean;
onPress: () => void
}) => {
return (
<TouchableOpacity
style={[styles.categoryTab, isSelected && styles.selectedCategoryTab]}
onPress={onPress}
>
<Text style={[styles.categoryText, isSelected && styles.selectedCategoryText]}>{title}</Text>
</TouchableOpacity>
);
};
const NewsPage: React.FC = () => {
const [selectedCategory, setSelectedCategory] = useState(0);
// 分类数据
const categories = ['推荐', '热点', '科技', '财经', '体育', '娱乐'];
// 新闻数据
const newsData = [
{
id: 1,
title: '人工智能技术在医疗领域的突破性进展',
summary: 'AI技术正在帮助医生诊断疾病,提高治疗效率,为患者带来更好的医疗服务体验。',
author: '科技日报',
time: '2小时前',
category: '科技',
readTime: '5分钟',
image: 'https://picsum.photos/300/200?random=1',
comments: 24,
likes: 128,
isBookmarked: true,
},
{
id: 2,
title: '全球股市迎来新一轮上涨行情',
summary: '受多重利好消息影响,全球主要股指普遍上涨,投资者信心显著增强。',
author: '财经周刊',
time: '4小时前',
category: '财经',
readTime: '3分钟',
image: 'https://picsum.photos/300/200?random=2',
comments: 18,
likes: 96,
isBookmarked: false,
},
{
id: 3,
title: '世界杯预选赛精彩瞬间回顾',
summary: '昨晚的比赛可谓精彩纷呈,多场关键战役决定了出线形势。',
author: '体育时报',
time: '6小时前',
category: '体育',
readTime: '4分钟',
image: 'https://picsum.photos/300/200?random=3',
comments: 32,
likes: 210,
isBookmarked: true,
},
{
id: 4,
title: '知名演员新电影票房破纪录',
summary: '该影片上映首周即打破多项票房记录,观众口碑持续走高。',
author: '娱乐新闻',
time: '8小时前',
category: '娱乐',
readTime: '2分钟',
image: 'https://picsum.photos/300/200?random=4',
comments: 15,
likes: 78,
isBookmarked: false,
},
{
id: 5,
title: '新能源汽车市场持续增长',
summary: '随着环保意识提升和技术进步,新能源汽车销量再创新高。',
author: '汽车杂志',
time: '10小时前',
category: '科技',
readTime: '6分钟',
image: 'https://picsum.photos/300/200?random=5',
comments: 27,
likes: 156,
isBookmarked: true,
},
];
return (
<SafeAreaView style={styles.container}>
{/* 头部 */}
<View style={styles.header}>
<Text style={styles.title}>新闻资讯</Text>
<Text style={styles.subtitle}>每日精选,及时获取最新资讯</Text>
</View>
{/* 搜索栏 */}
<SearchBar />
{/* 分类标签 */}
<ScrollView
horizontal
showsHorizontalScrollIndicator={false}
style={styles.categoryScrollView}
contentContainerStyle={styles.categoryScrollContent}
>
{categories.map((category, index) => (
<CategoryTab
key={index}
title={category}
isSelected={selectedCategory === index}
onPress={() => setSelectedCategory(index)}
/>
))}
</ScrollView>
{/* 新闻列表 */}
<ScrollView style={styles.newsList}>
{newsData.map(news => (
<NewsCard
key={news.id}
title={news.title}
summary={news.summary}
author={news.author}
time={news.time}
category={news.category}
readTime={news.readTime}
image={news.image}
comments={news.comments}
likes={news.likes}
isBookmarked={news.isBookmarked}
/>
))}
</ScrollView>
{/* 底部导航 */}
<View style={styles.bottomNav}>
<TouchableOpacity style={styles.navItem}>
<Text style={[styles.navIcon, styles.activeNavIcon]}>{ICONS.home}</Text>
<Text style={[styles.navText, styles.activeNavText]}>首页</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.navItem}>
<Text style={styles.navIcon}>{ICONS.news}</Text>
<Text style={styles.navText}>新闻</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.navItem}>
<Text style={styles.navIcon}>{ICONS.bookmark}</Text>
<Text style={styles.navText}>收藏</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.navItem}>
<Text style={styles.navIcon}>{ICONS.user}</Text>
<Text style={styles.navText}>我的</Text>
</TouchableOpacity>
</View>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f8fafc',
},
header: {
padding: 20,
backgroundColor: '#ffffff',
borderBottomWidth: 1,
borderBottomColor: '#e2e8f0',
},
title: {
fontSize: 24,
fontWeight: 'bold',
color: '#1e293b',
marginBottom: 4,
},
subtitle: {
fontSize: 14,
color: '#64748b',
},
searchBar: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: '#ffffff',
margin: 16,
paddingHorizontal: 16,
paddingVertical: 12,
borderRadius: 8,
elevation: 1,
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.1,
shadowRadius: 2,
},
searchIcon: {
fontSize: 18,
color: '#94a3b8',
marginRight: 10,
},
searchPlaceholder: {
fontSize: 16,
color: '#94a3b8',
flex: 1,
},
categoryScrollView: {
backgroundColor: '#ffffff',
paddingVertical: 12,
paddingLeft: 16,
},
categoryScrollContent: {
paddingRight: 16,
},
categoryTab: {
paddingHorizontal: 16,
paddingVertical: 8,
borderRadius: 20,
backgroundColor: '#f1f5f9',
marginRight: 10,
},
selectedCategoryTab: {
backgroundColor: '#3b82f6',
},
categoryText: {
fontSize: 14,
color: '#64748b',
fontWeight: '500',
},
selectedCategoryText: {
color: '#ffffff',
},
newsList: {
flex: 1,
padding: 16,
},
newsCard: {
backgroundColor: '#ffffff',
borderRadius: 12,
marginBottom: 16,
overflow: 'hidden',
elevation: 2,
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.1,
shadowRadius: 2,
},
newsImage: {
width: '100%',
height: 180,
resizeMode: 'cover',
},
newsContent: {
padding: 16,
},
newsHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
marginBottom: 8,
},
categoryTag: {
backgroundColor: '#dbeafe',
color: '#1d4ed8',
fontSize: 12,
fontWeight: '500',
paddingHorizontal: 8,
paddingVertical: 4,
borderRadius: 12,
},
readTime: {
fontSize: 12,
color: '#94a3b8',
},
newsTitle: {
fontSize: 18,
fontWeight: 'bold',
color: '#1e293b',
marginBottom: 8,
lineHeight: 24,
},
newsSummary: {
fontSize: 14,
color: '#64748b',
lineHeight: 20,
marginBottom: 12,
},
newsFooter: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
},
authorInfo: {
flex: 1,
},
author: {
fontSize: 14,
color: '#334155',
fontWeight: '500',
},
time: {
fontSize: 12,
color: '#94a3b8',
},
newsActions: {
flexDirection: 'row',
},
actionButton: {
marginLeft: 16,
},
actionText: {
fontSize: 14,
color: '#64748b',
},
bottomNav: {
flexDirection: 'row',
justifyContent: 'space-around',
backgroundColor: '#ffffff',
borderTopWidth: 1,
borderTopColor: '#e2e8f0',
paddingVertical: 12,
},
navItem: {
alignItems: 'center',
},
activeNavItem: {
paddingBottom: 2,
borderBottomWidth: 2,
borderBottomColor: '#3b82f6',
},
navIcon: {
fontSize: 20,
color: '#94a3b8',
marginBottom: 4,
},
activeNavIcon: {
color: '#3b82f6',
},
navText: {
fontSize: 12,
color: '#94a3b8',
},
activeNavText: {
color: '#3b82f6',
fontWeight: '500',
},
});
export default NewsPage;

打包
接下来通过打包命令npn run harmony将reactNative的代码打包成为bundle,这样可以进行在开源鸿蒙OpenHarmony中进行使用。

打包之后再将打包后的鸿蒙OpenHarmony文件拷贝到鸿蒙的DevEco-Studio工程目录去:

最后运行效果图如下显示:

本文分析了一个基于React Native开发的新闻资讯应用,重点探讨其组件化架构、状态管理和跨平台实现。应用采用清晰的组件拆分(NewsCard、SearchBar等),通过React Hooks管理状态,并使用响应式设计适配不同屏幕。文章特别关注了该应用在鸿蒙系统上的跨端适配策略,包括性能优化、事件处理和图片加载等关键问题。案例展示了如何在保持代码可维护性的同时实现跨平台一致性,为类似应用开发提供了参考方案。
欢迎大家加入开源鸿蒙跨平台开发者社区,一起共建开源鸿蒙跨平台生态。
更多推荐




所有评论(0)