React Native鸿蒙跨平台采用 React.memo 包裹 KnowledgeCard,并将 item 拆分成原子 prop(或使用 selector)以减少无关字段变化导致的子项重渲染
类型建模与数据语义
- 用 TypeScript 为 KnowledgeItem、Category 定义结构,是跨端一致性的“协议层”。所有组件只消费这些纯 JS 数据,无平台特化字段,Bridge 只传递简单对象,鸿蒙端与 iOS/Android 的表现更可控。
- 页面级数据采用 useState 初始化静态集合(knowledgeItems、categories、stats),避免副作用订阅和异步流。初始渲染主要消耗在视图树构建,不会产生额外桥接抖动,利于验证 ArkUI 后端的基础合成性能。
不可变更新与函数式 setState
- 点赞与收藏使用函数式更新 setKnowledgeItems(items => …),从闭包中获取最新快照,避免“旧值覆盖”问题。这类不可变更新是 RN 跨端的可靠基线:只有明确的 setState 才会触发原生树批量重排,三端一致。
- onStar 直接对 isStarred 做布尔翻转,遵循“语义最小化变更”,减少桥上传输的对象大小,微优化在大量列表更新时更明显。
列表与虚拟化的正确打开方式
- 知识条目使用 FlatList 并以 id 作为稳定 key,走虚拟化与窗口化渲染,对 ArkUI/Skia 合成路径更友好;在数据规模扩大时,可开启 removeClippedSubviews、调整 windowSize 与 initialNumToRender,降低首屏与滚动抖动。
- 页面将 FlatList 包在一个外层 ScrollView 里,这在 RN 是典型的性能与交互反模式:嵌套滚动会让两套滚动容器竞争事件并重复布局。跨端方案是让 FlatList“接管整页”,其余头部内容通过 ListHeaderComponent 注入,底部通过 ListFooterComponent 注入,达到单一滚动容器。
import React, { useCallback, memo } from 'react';
import { SafeAreaView, FlatList, View, Text, TouchableOpacity, Alert } from 'react-native';
const KnowledgeCard = memo(({ item, onLike, onComment, onStar }) => (
<View>
<View>
<TouchableOpacity onPress={() => onStar(item.id)}>
<Text>{item.isStarred ? '⭐' : '☆'}</Text>
</TouchableOpacity>
</View>
<Text>{item.title}</Text>
<Text numberOfLines={3}>{item.content}</Text>
<View>
<TouchableOpacity onPress={() => onLike(item.id)}>
<Text>👍 {item.likes}</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => onComment(item.id)}>
<Text>💬 {item.comments}</Text>
</TouchableOpacity>
</View>
</View>
));
const Page = ({ items, categories, stats, onLike, onComment, onStar, onCategoryPress, onSearch, onShare }) => {
const renderItem = useCallback(({ item }) => (
<KnowledgeCard item={item} onLike={onLike} onComment={onComment} onStar={onStar} />
), [onLike, onComment, onStar]);
const Header = (
<View>
<View>
<Text>团队知识库</Text>
<View>
<TouchableOpacity onPress={onSearch}><Text>🔍</Text></TouchableOpacity>
<TouchableOpacity onPress={onShare}><Text>📤 分享</Text></TouchableOpacity>
</View>
</View>
<View>
{stats.map((s, i) => (
<View key={i}><Text>{s.icon} {s.value} {s.title}</Text></View>
))}
</View>
<Text>知识类别</Text>
<ScrollView horizontal showsHorizontalScrollIndicator={false}>
<View>
{categories.map(c => (
<TouchableOpacity key={c.id} onPress={() => onCategoryPress(c.name)}>
<Text>📝 {c.name} ({c.count})</Text>
</TouchableOpacity>
))}
</View>
</ScrollView>
<Text>最新分享 ({items.length})</Text>
</View>
);
return (
<SafeAreaView>
<FlatList
data={items}
keyExtractor={item => item.id}
renderItem={renderItem}
ListHeaderComponent={Header}
showsVerticalScrollIndicator={false}
removeClippedSubviews
initialNumToRender={6}
windowSize={5}
/>
</SafeAreaView>
);
};
桥接与原生能力:Alert 与事件语义
- Alert 映射为原生弹窗(iOS UIAlertController、Android AlertDialog、鸿蒙 ArkUI 对应弹窗),JS 回调在原生处理完成后回到 JS 线程。当前使用简单提示,不涉及多次 setState 合并;若需在确认后批量更新列表,建议在回调中做一次性合并,减少桥面出入栈。
- 交互闭包只传递基本标识(id),函数引用稳定,RN Bridge 负载轻;在高频交互场景,请配合 useCallback 固定 renderItem 与事件处理器,降低列表行的重渲染频率。
图标策略与跨端一致性
- 采用 Emoji 文本作为图标,优点是零依赖、无桥接负担;缺点是不同平台字体渲染差异明显。若追求一致视觉与可定制色彩,推荐统一到 react-native-svg 或图标字体,鸿蒙端需确认 SVG/字体桥接的完整性与性能。
- 业务上图标变更频繁时,矢量路径或图标字库能减少 UI 改版成本;Emoji 适合原型期与低复杂度场景。
测量与布局计算的稳定化
- statCard 的宽度以 Dimensions.get(‘window’) 驱动,属于一次性尺寸查询。在鸿蒙端与大屏设备,建议引入 onLayout 结合 getItemLayout,为列表项提供稳定测量,FlatList 的滚动定位与回收策略更精确。
- 知识卡片高度大致可预估;提供 getItemLayout 能让 RN 在三端避免“滚到位置→再测量→再校正”的跳动,尤其在 ArkUI 后端中提升滚动的确定性。
const ITEM_HEIGHT = 160;
const getItemLayout = (_: any, index: number) => ({ length: ITEM_HEIGHT, offset: ITEM_HEIGHT * index, index });
性能与可扩展的演进路径
- 当点赞/收藏变更频繁,采用 React.memo 包裹 KnowledgeCard,并将 item 拆分成原子 prop(或使用 selector)以减少无关字段变化导致的子项重渲染。
- 大数据场景下启用分页与 onEndReached,配合批量 append(一次 setState 合并多条),减少桥面消息与原生树增量开销,三端滚动更平滑。
- 动效需求(如收藏星星闪烁、列表入场)采用 Animated 或 Reanimated,并选择 useNativeDriver 的 transform/opacity 属性,让 UI 线程承担插值,避免 JS 定时器不稳定;鸿蒙端同样受益于原生驱动合成层更新。
跨端实践要点
- 单一滚动容器:让 FlatList 承担页面滚动,其他模块注入 Header/Footer,避免嵌套 ScrollView 的事件竞争与重复布局。
- 不可变数据与函数式更新:保证每次交互都有明确的状态信号,Bridge 批处理更稳定;高频交互合并 setState,降低消息风暴。
在现代移动应用开发中,跨平台框架的选择往往决定了开发效率和最终用户体验。React Native 作为业界主流的跨端解决方案,其在鸿蒙生态中的应用前景备受关注。本文将以一个团队知识库应用为例,深入剖析其中关键技术点的设计与实现。
组件声明与类型定义的艺术
应用采用了 TypeScript 进行类型定义,这是大型 React Native 项目的标配。类型系统的引入不仅提升了代码的可维护性,更在跨平台开发中发挥了至关重要的作用。
type KnowledgeItem = {
id: string;
title: string;
content: string;
author: string;
category: string;
tags: string[];
likes: number;
comments: number;
sharedBy: string;
sharedAt: string;
isStarred: boolean;
};
这种细粒度的类型定义确保了数据结构的一致性。在跨平台开发中,数据类型的一致性往往是减少平台差异带来问题的关键。当代码需要在不同平台(Android、iOS、HarmonyOS)运行时,统一的数据契约可以避免许多潜在的类型转换错误。
状态管理的函数式迭代
React Hooks 的使用体现了现代函数式编程思想。useState 不仅用于管理简单状态,更能够处理复杂的数据结构更新:
const handleStar = (id: string) => {
setKnowledgeItems(items =>
items.map(item =>
item.id === id ? { ...item, isStarred: !item.isStarred } : item
)
);
};
这种不可变数据更新的模式在跨平台场景下尤为重要。不同平台的内存管理机制各不相同,但不可变数据的原则可以确保状态更新的可预测性,减少因为平台差异导致的状态同步问题。
组件间通信的设计哲学
KnowledgeCard 组件展示了清晰的数据流设计模式:
const KnowledgeCard = ({
item,
onLike,
onComment,
onStar
}: {
item: KnowledgeItem;
onLike: (id: string) => void;
onComment: (id: string) => void;
onStar: (id: string) => void;
}) => {
// 组件实现
};
这种设计模式将数据(item)和操作(onLike、onComment、onStar)明确分离,符合单向数据流的原则。在跨平台开发中,这种清晰的接口定义使得组件可以在不同平台上复用,而无需关心底层平台的实现细节。
列表渲染的性能考量
应用使用了 FlatList 组件来展示知识条目,这是 React Native 推荐的高性能列表组件:
<FlatList
data={knowledgeItems}
keyExtractor={item => item.id}
renderItem={({ item }) => (
<KnowledgeCard
item={item}
onLike={handleLike}
onComment={handleComment}
onStar={handleStar}
/>
)}
showsVerticalScrollIndicator={false}
/>
在鸿蒙平台上,虽然底层的渲染引擎可能有所不同,但 React Native 的 FlatList 组件通过虚拟化技术确保了列表渲染的性能。这种跨平台的一致性使得开发者无需为不同平台编写不同的列表实现。
交互反馈的统一处理
组件中的交互处理采用了统一的 Alert 机制:
const handleComment = (id: string) => {
Alert.alert('评论', `为知识条目 ${knowledgeItems.find(k => k.id === id)?.title} 添加评论`);
};
虽然这种实现方式在用户体验上可能不够原生,但在跨平台开发中,统一的交互模式可以减少平台适配的工作量。在实际项目中,可以根据目标平台的特性选择是否使用原生弹窗组件。
导航结构的抽象思考
底部导航栏的实现展示了简单的导航结构:
<View style={styles.bottomNav}>
<TouchableOpacity style={styles.navItem}>
<Text style={styles.navIcon}>{ICONS.knowledge}</Text>
<Text style={styles.navText}>知识库</Text>
</TouchableOpacity>
{/* 其他导航项 */}
</View>
在真实的跨平台应用中,通常会使用 React Navigation 等成熟的导航库。这些库提供了统一的导航 API,底层会根据不同平台使用原生导航组件,既保证了性能,又提供了平台特定的用户体验。
真实演示案例代码:
// app.tsx
import React, { useState } from 'react';
import { SafeAreaView, View, Text, StyleSheet, TouchableOpacity, ScrollView, Dimensions, Alert, FlatList } from 'react-native';
// 图标库
const ICONS = {
knowledge: '📚',
share: '📤',
user: '👥',
article: '📝',
search: '🔍',
star: '⭐',
like: '👍',
comment: '💬',
};
const { width } = Dimensions.get('window');
// 知识条目类型
type KnowledgeItem = {
id: string;
title: string;
content: string;
author: string;
category: string;
tags: string[];
likes: number;
comments: number;
sharedBy: string;
sharedAt: string;
isStarred: boolean;
};
// 类别类型
type Category = {
id: string;
name: string;
count: number;
icon: string;
};
// 知识条目卡片组件
const KnowledgeCard = ({
item,
onLike,
onComment,
onStar
}: {
item: KnowledgeItem;
onLike: (id: string) => void;
onComment: (id: string) => void;
onStar: (id: string) => void;
}) => {
return (
<View style={styles.knowledgeCard}>
<View style={styles.cardHeader}>
<View style={styles.authorInfo}>
<Text style={styles.authorAvatar}>{ICONS.user}</Text>
<View>
<Text style={styles.authorName}>{item.author}</Text>
<Text style={styles.sharedInfo}>分享自: {item.sharedBy} • {item.sharedAt}</Text>
</View>
</View>
<TouchableOpacity onPress={() => onStar(item.id)}>
<Text style={styles.starIcon}>{item.isStarred ? ICONS.star : '☆'}</Text>
</TouchableOpacity>
</View>
<Text style={styles.cardTitle}>{item.title}</Text>
<Text style={styles.cardContent} numberOfLines={3}>{item.content}</Text>
<View style={styles.tagContainer}>
<Text style={styles.categoryTag}>{item.category}</Text>
{item.tags.slice(0, 2).map((tag, index) => (
<Text key={index} style={styles.tag}>#{tag}</Text>
))}
</View>
<View style={styles.cardFooter}>
<TouchableOpacity style={styles.actionButton} onPress={() => onLike(item.id)}>
<Text style={styles.actionIcon}>{ICONS.like}</Text>
<Text style={styles.actionText}>{item.likes}</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.actionButton} onPress={() => onComment(item.id)}>
<Text style={styles.actionIcon}>{ICONS.comment}</Text>
<Text style={styles.actionText}>{item.comments}</Text>
</TouchableOpacity>
<Text style={styles.actionText}>{item.sharedAt}</Text>
</View>
</View>
);
};
// 统计卡片组件
const StatCard = ({
title,
value,
icon
}: {
title: string;
value: string | number;
icon: string
}) => {
return (
<View style={styles.statCard}>
<Text style={styles.statIcon}>{icon}</Text>
<View style={styles.statInfo}>
<Text style={styles.statValue}>{value}</Text>
<Text style={styles.statTitle}>{title}</Text>
</View>
</View>
);
};
// 类别卡片组件
const CategoryCard = ({
category,
onPress
}: {
category: Category;
onPress: () => void
}) => {
return (
<TouchableOpacity style={styles.categoryCard} onPress={onPress}>
<Text style={styles.categoryIcon}>{category.icon}</Text>
<Text style={styles.categoryName}>{category.name}</Text>
<Text style={styles.categoryCount}>({category.count})</Text>
</TouchableOpacity>
);
};
// 主页面组件
const KnowledgeSharingPlatformApp: React.FC = () => {
const [knowledgeItems, setKnowledgeItems] = useState<KnowledgeItem[]>([
{
id: '1',
title: 'React Native 性能优化指南',
content: '本文详细介绍了React Native应用性能优化的各种技巧和最佳实践,包括虚拟化列表、内存管理、渲染优化等方面的内容...',
author: '张工程师',
category: '前端开发',
tags: ['React', '性能优化', '移动端'],
likes: 42,
comments: 8,
sharedBy: '李总监',
sharedAt: '2小时前',
isStarred: true
},
{
id: '2',
title: '数据库索引设计最佳实践',
content: '数据库索引是提升查询性能的关键因素之一。本文将详细介绍如何设计高效的数据库索引,包括单列索引、复合索引、覆盖索引等...',
author: '王架构师',
category: '后端开发',
tags: ['数据库', '索引', '性能'],
likes: 36,
comments: 5,
sharedBy: '陈经理',
sharedAt: '5小时前',
isStarred: false
},
{
id: '3',
title: '敏捷开发流程详解',
content: '敏捷开发是一种迭代式的软件开发方法论,强调快速交付、持续改进和团队协作。本文将深入解析敏捷开发的核心原则和实践...',
author: '刘项目经理',
category: '项目管理',
tags: ['敏捷', '项目管理', '团队协作'],
likes: 28,
comments: 12,
sharedBy: '赵主管',
sharedAt: '1天前',
isStarred: true
},
{
id: '4',
title: '用户体验设计原则',
content: '良好的用户体验是产品成功的关键因素。本文总结了用户体验设计的核心原则,包括可用性、可访问性、一致性等方面...',
author: '孙设计师',
category: 'UI/UX',
tags: ['用户体验', '界面设计', '交互'],
likes: 51,
comments: 15,
sharedBy: '周总监',
sharedAt: '2天前',
isStarred: false
}
]);
const [categories] = useState<Category[]>([
{ id: '1', name: '前端开发', count: 42, icon: ICONS.article },
{ id: '2', name: '后端开发', count: 38, icon: ICONS.article },
{ id: '3', name: '项目管理', count: 24, icon: ICONS.article },
{ id: '4', name: 'UI/UX', count: 18, icon: ICONS.article },
{ id: '5', name: '数据分析', count: 15, icon: ICONS.article },
{ id: '6', name: '运维安全', count: 12, icon: ICONS.article },
]);
const [stats] = useState([
{ title: '知识条目', value: 124, icon: ICONS.knowledge },
{ title: '团队成员', value: 36, icon: ICONS.user },
{ title: '本月分享', value: 18, icon: ICONS.share },
{ title: '本月点赞', value: 245, icon: ICONS.like },
]);
const handleLike = (id: string) => {
setKnowledgeItems(items =>
items.map(item =>
item.id === id ? { ...item, likes: item.likes + 1 } : item
)
);
};
const handleComment = (id: string) => {
Alert.alert('评论', `为知识条目 ${knowledgeItems.find(k => k.id === id)?.title} 添加评论`);
};
const handleStar = (id: string) => {
setKnowledgeItems(items =>
items.map(item =>
item.id === id ? { ...item, isStarred: !item.isStarred } : item
)
);
};
const handleCategoryPress = (name: string) => {
Alert.alert('类别筛选', `显示 ${name} 类别的知识条目`);
};
const handleSearch = () => {
Alert.alert('搜索', '打开知识库搜索功能');
};
const handleShare = () => {
Alert.alert('分享知识', '选择要分享的知识内容');
};
return (
<SafeAreaView style={styles.container}>
{/* 头部 */}
<View style={styles.header}>
<Text style={styles.title}>团队知识库</Text>
<View style={styles.headerActions}>
<TouchableOpacity style={styles.searchButton} onPress={handleSearch}>
<Text style={styles.searchIcon}>{ICONS.search}</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.shareButton} onPress={handleShare}>
<Text style={styles.shareText}>{ICONS.share} 分享</Text>
</TouchableOpacity>
</View>
</View>
{/* 统计卡片 */}
<ScrollView style={styles.content}>
<View style={styles.statsContainer}>
{stats.map((stat, index) => (
<StatCard
key={index}
title={stat.title}
value={stat.value}
icon={stat.icon}
/>
))}
</View>
{/* 类别列表 */}
<Text style={styles.sectionTitle}>知识类别</Text>
<ScrollView horizontal showsHorizontalScrollIndicator={false} style={styles.categoriesContainer}>
<View style={styles.categoriesList}>
{categories.map(category => (
<CategoryCard
key={category.id}
category={category}
onPress={() => handleCategoryPress(category.name)}
/>
))}
</View>
</ScrollView>
{/* 知识条目列表 */}
<Text style={styles.sectionTitle}>最新分享 ({knowledgeItems.length})</Text>
<FlatList
data={knowledgeItems}
keyExtractor={item => item.id}
renderItem={({ item }) => (
<KnowledgeCard
item={item}
onLike={handleLike}
onComment={handleComment}
onStar={handleStar}
/>
)}
showsVerticalScrollIndicator={false}
/>
{/* 操作按钮 */}
<View style={styles.actionButtons}>
<TouchableOpacity style={styles.fabButton}>
<Text style={styles.fabIcon}>{ICONS.search}</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.fabButton}>
<Text style={styles.fabIcon}>{ICONS.share}</Text>
</TouchableOpacity>
</View>
</ScrollView>
{/* 底部导航 */}
<View style={styles.bottomNav}>
<TouchableOpacity style={styles.navItem}>
<Text style={styles.navIcon}>{ICONS.knowledge}</Text>
<Text style={styles.navText}>知识库</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.navItem}>
<Text style={styles.navIcon}>{ICONS.article}</Text>
<Text style={styles.navText}>文章</Text>
</TouchableOpacity>
<TouchableOpacity style={[styles.navItem, styles.activeNavItem]}>
<Text style={styles.navIcon}>{ICONS.star}</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: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
padding: 20,
backgroundColor: '#ffffff',
borderBottomWidth: 1,
borderBottomColor: '#e2e8f0',
},
title: {
fontSize: 20,
fontWeight: 'bold',
color: '#1e293b',
},
headerActions: {
flexDirection: 'row',
alignItems: 'center',
},
searchButton: {
width: 36,
height: 36,
borderRadius: 18,
backgroundColor: '#f1f5f9',
alignItems: 'center',
justifyContent: 'center',
marginRight: 12,
},
searchIcon: {
fontSize: 18,
color: '#64748b',
},
shareButton: {
backgroundColor: '#3b82f6',
paddingHorizontal: 16,
paddingVertical: 8,
borderRadius: 20,
},
shareText: {
color: '#ffffff',
fontSize: 14,
fontWeight: '500',
},
content: {
flex: 1,
padding: 16,
},
statsContainer: {
flexDirection: 'row',
flexWrap: 'wrap',
justifyContent: 'space-between',
marginBottom: 16,
},
statCard: {
backgroundColor: '#ffffff',
borderRadius: 12,
padding: 16,
width: (width - 48) / 2,
marginBottom: 12,
elevation: 1,
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.1,
shadowRadius: 2,
flexDirection: 'row',
alignItems: 'center',
},
statIcon: {
fontSize: 24,
color: '#3b82f6',
marginRight: 12,
},
statInfo: {},
statValue: {
fontSize: 20,
fontWeight: 'bold',
color: '#1e293b',
},
statTitle: {
fontSize: 12,
color: '#64748b',
marginTop: 4,
},
sectionTitle: {
fontSize: 18,
fontWeight: 'bold',
color: '#1e293b',
marginVertical: 12,
},
categoriesContainer: {
marginBottom: 16,
},
categoriesList: {
flexDirection: 'row',
},
categoryCard: {
backgroundColor: '#ffffff',
borderRadius: 12,
padding: 12,
marginRight: 12,
alignItems: 'center',
minWidth: 80,
elevation: 1,
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.1,
shadowRadius: 2,
},
categoryIcon: {
fontSize: 24,
color: '#3b82f6',
marginBottom: 8,
},
categoryName: {
fontSize: 12,
color: '#1e293b',
textAlign: 'center',
},
categoryCount: {
fontSize: 10,
color: '#64748b',
marginTop: 4,
},
knowledgeCard: {
backgroundColor: '#ffffff',
borderRadius: 12,
padding: 16,
marginBottom: 16,
elevation: 1,
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.1,
shadowRadius: 2,
},
cardHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 12,
},
authorInfo: {
flexDirection: 'row',
alignItems: 'center',
},
authorAvatar: {
width: 32,
height: 32,
borderRadius: 16,
backgroundColor: '#dbeafe',
alignItems: 'center',
justifyContent: 'center',
marginRight: 12,
},
authorName: {
fontSize: 14,
fontWeight: 'bold',
color: '#1e293b',
},
sharedInfo: {
fontSize: 12,
color: '#64748b',
},
starIcon: {
fontSize: 20,
color: '#f59e0b',
},
cardTitle: {
fontSize: 16,
fontWeight: 'bold',
color: '#1e293b',
marginBottom: 8,
},
cardContent: {
fontSize: 14,
color: '#334155',
lineHeight: 20,
marginBottom: 12,
},
tagContainer: {
flexDirection: 'row',
flexWrap: 'wrap',
marginBottom: 12,
},
categoryTag: {
backgroundColor: '#dbeafe',
color: '#3b82f6',
fontSize: 12,
paddingHorizontal: 8,
paddingVertical: 4,
borderRadius: 12,
marginRight: 8,
marginBottom: 4,
},
tag: {
backgroundColor: '#f1f5f9',
color: '#64748b',
fontSize: 12,
paddingHorizontal: 8,
paddingVertical: 4,
borderRadius: 12,
marginRight: 8,
marginBottom: 4,
},
cardFooter: {
flexDirection: 'row',
alignItems: 'center',
borderTopWidth: 1,
borderTopColor: '#e2e8f0',
paddingTop: 12,
},
actionButton: {
flexDirection: 'row',
alignItems: 'center',
marginRight: 16,
},
actionIcon: {
fontSize: 16,
color: '#64748b',
marginRight: 4,
},
actionText: {
fontSize: 12,
color: '#64748b',
},
actionButtons: {
position: 'absolute',
right: 20,
bottom: 80,
flexDirection: 'column',
},
fabButton: {
width: 50,
height: 50,
borderRadius: 25,
backgroundColor: '#3b82f6',
alignItems: 'center',
justifyContent: 'center',
marginBottom: 12,
elevation: 3,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.25,
shadowRadius: 4,
},
fabIcon: {
fontSize: 20,
color: '#ffffff',
},
bottomNav: {
flexDirection: 'row',
justifyContent: 'space-around',
backgroundColor: '#ffffff',
borderTopWidth: 1,
borderTopColor: '#e2e8f0',
paddingVertical: 12,
},
navItem: {
alignItems: 'center',
flex: 1,
},
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 KnowledgeSharingPlatformApp;// app.tsx
import React, { useState } from 'react';
import { SafeAreaView, View, Text, StyleSheet, TouchableOpacity, ScrollView, Dimensions, Alert, FlatList } from 'react-native';
// 图标库
const ICONS = {
knowledge: '📚',
share: '📤',
user: '👥',
article: '📝',
search: '🔍',
star: '⭐',
like: '👍',
comment: '💬',
};
const { width } = Dimensions.get('window');
// 知识条目类型
type KnowledgeItem = {
id: string;
title: string;
content: string;
author: string;
category: string;
tags: string[];
likes: number;
comments: number;
sharedBy: string;
sharedAt: string;
isStarred: boolean;
};
// 类别类型
type Category = {
id: string;
name: string;
count: number;
icon: string;
};
// 知识条目卡片组件
const KnowledgeCard = ({
item,
onLike,
onComment,
onStar
}: {
item: KnowledgeItem;
onLike: (id: string) => void;
onComment: (id: string) => void;
onStar: (id: string) => void;
}) => {
return (
<View style={styles.knowledgeCard}>
<View style={styles.cardHeader}>
<View style={styles.authorInfo}>
<Text style={styles.authorAvatar}>{ICONS.user}</Text>
<View>
<Text style={styles.authorName}>{item.author}</Text>
<Text style={styles.sharedInfo}>分享自: {item.sharedBy} • {item.sharedAt}</Text>
</View>
</View>
<TouchableOpacity onPress={() => onStar(item.id)}>
<Text style={styles.starIcon}>{item.isStarred ? ICONS.star : '☆'}</Text>
</TouchableOpacity>
</View>
<Text style={styles.cardTitle}>{item.title}</Text>
<Text style={styles.cardContent} numberOfLines={3}>{item.content}</Text>
<View style={styles.tagContainer}>
<Text style={styles.categoryTag}>{item.category}</Text>
{item.tags.slice(0, 2).map((tag, index) => (
<Text key={index} style={styles.tag}>#{tag}</Text>
))}
</View>
<View style={styles.cardFooter}>
<TouchableOpacity style={styles.actionButton} onPress={() => onLike(item.id)}>
<Text style={styles.actionIcon}>{ICONS.like}</Text>
<Text style={styles.actionText}>{item.likes}</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.actionButton} onPress={() => onComment(item.id)}>
<Text style={styles.actionIcon}>{ICONS.comment}</Text>
<Text style={styles.actionText}>{item.comments}</Text>
</TouchableOpacity>
<Text style={styles.actionText}>{item.sharedAt}</Text>
</View>
</View>
);
};
// 统计卡片组件
const StatCard = ({
title,
value,
icon
}: {
title: string;
value: string | number;
icon: string
}) => {
return (
<View style={styles.statCard}>
<Text style={styles.statIcon}>{icon}</Text>
<View style={styles.statInfo}>
<Text style={styles.statValue}>{value}</Text>
<Text style={styles.statTitle}>{title}</Text>
</View>
</View>
);
};
// 类别卡片组件
const CategoryCard = ({
category,
onPress
}: {
category: Category;
onPress: () => void
}) => {
return (
<TouchableOpacity style={styles.categoryCard} onPress={onPress}>
<Text style={styles.categoryIcon}>{category.icon}</Text>
<Text style={styles.categoryName}>{category.name}</Text>
<Text style={styles.categoryCount}>({category.count})</Text>
</TouchableOpacity>
);
};
// 主页面组件
const KnowledgeSharingPlatformApp: React.FC = () => {
const [knowledgeItems, setKnowledgeItems] = useState<KnowledgeItem[]>([
{
id: '1',
title: 'React Native 性能优化指南',
content: '本文详细介绍了React Native应用性能优化的各种技巧和最佳实践,包括虚拟化列表、内存管理、渲染优化等方面的内容...',
author: '张工程师',
category: '前端开发',
tags: ['React', '性能优化', '移动端'],
likes: 42,
comments: 8,
sharedBy: '李总监',
sharedAt: '2小时前',
isStarred: true
},
{
id: '2',
title: '数据库索引设计最佳实践',
content: '数据库索引是提升查询性能的关键因素之一。本文将详细介绍如何设计高效的数据库索引,包括单列索引、复合索引、覆盖索引等...',
author: '王架构师',
category: '后端开发',
tags: ['数据库', '索引', '性能'],
likes: 36,
comments: 5,
sharedBy: '陈经理',
sharedAt: '5小时前',
isStarred: false
},
{
id: '3',
title: '敏捷开发流程详解',
content: '敏捷开发是一种迭代式的软件开发方法论,强调快速交付、持续改进和团队协作。本文将深入解析敏捷开发的核心原则和实践...',
author: '刘项目经理',
category: '项目管理',
tags: ['敏捷', '项目管理', '团队协作'],
likes: 28,
comments: 12,
sharedBy: '赵主管',
sharedAt: '1天前',
isStarred: true
},
{
id: '4',
title: '用户体验设计原则',
content: '良好的用户体验是产品成功的关键因素。本文总结了用户体验设计的核心原则,包括可用性、可访问性、一致性等方面...',
author: '孙设计师',
category: 'UI/UX',
tags: ['用户体验', '界面设计', '交互'],
likes: 51,
comments: 15,
sharedBy: '周总监',
sharedAt: '2天前',
isStarred: false
}
]);
const [categories] = useState<Category[]>([
{ id: '1', name: '前端开发', count: 42, icon: ICONS.article },
{ id: '2', name: '后端开发', count: 38, icon: ICONS.article },
{ id: '3', name: '项目管理', count: 24, icon: ICONS.article },
{ id: '4', name: 'UI/UX', count: 18, icon: ICONS.article },
{ id: '5', name: '数据分析', count: 15, icon: ICONS.article },
{ id: '6', name: '运维安全', count: 12, icon: ICONS.article },
]);
const [stats] = useState([
{ title: '知识条目', value: 124, icon: ICONS.knowledge },
{ title: '团队成员', value: 36, icon: ICONS.user },
{ title: '本月分享', value: 18, icon: ICONS.share },
{ title: '本月点赞', value: 245, icon: ICONS.like },
]);
const handleLike = (id: string) => {
setKnowledgeItems(items =>
items.map(item =>
item.id === id ? { ...item, likes: item.likes + 1 } : item
)
);
};
const handleComment = (id: string) => {
Alert.alert('评论', `为知识条目 ${knowledgeItems.find(k => k.id === id)?.title} 添加评论`);
};
const handleStar = (id: string) => {
setKnowledgeItems(items =>
items.map(item =>
item.id === id ? { ...item, isStarred: !item.isStarred } : item
)
);
};
const handleCategoryPress = (name: string) => {
Alert.alert('类别筛选', `显示 ${name} 类别的知识条目`);
};
const handleSearch = () => {
Alert.alert('搜索', '打开知识库搜索功能');
};
const handleShare = () => {
Alert.alert('分享知识', '选择要分享的知识内容');
};
return (
<SafeAreaView style={styles.container}>
{/* 头部 */}
<View style={styles.header}>
<Text style={styles.title}>团队知识库</Text>
<View style={styles.headerActions}>
<TouchableOpacity style={styles.searchButton} onPress={handleSearch}>
<Text style={styles.searchIcon}>{ICONS.search}</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.shareButton} onPress={handleShare}>
<Text style={styles.shareText}>{ICONS.share} 分享</Text>
</TouchableOpacity>
</View>
</View>
{/* 统计卡片 */}
<ScrollView style={styles.content}>
<View style={styles.statsContainer}>
{stats.map((stat, index) => (
<StatCard
key={index}
title={stat.title}
value={stat.value}
icon={stat.icon}
/>
))}
</View>
{/* 类别列表 */}
<Text style={styles.sectionTitle}>知识类别</Text>
<ScrollView horizontal showsHorizontalScrollIndicator={false} style={styles.categoriesContainer}>
<View style={styles.categoriesList}>
{categories.map(category => (
<CategoryCard
key={category.id}
category={category}
onPress={() => handleCategoryPress(category.name)}
/>
))}
</View>
</ScrollView>
{/* 知识条目列表 */}
<Text style={styles.sectionTitle}>最新分享 ({knowledgeItems.length})</Text>
<FlatList
data={knowledgeItems}
keyExtractor={item => item.id}
renderItem={({ item }) => (
<KnowledgeCard
item={item}
onLike={handleLike}
onComment={handleComment}
onStar={handleStar}
/>
)}
showsVerticalScrollIndicator={false}
/>
{/* 操作按钮 */}
<View style={styles.actionButtons}>
<TouchableOpacity style={styles.fabButton}>
<Text style={styles.fabIcon}>{ICONS.search}</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.fabButton}>
<Text style={styles.fabIcon}>{ICONS.share}</Text>
</TouchableOpacity>
</View>
</ScrollView>
{/* 底部导航 */}
<View style={styles.bottomNav}>
<TouchableOpacity style={styles.navItem}>
<Text style={styles.navIcon}>{ICONS.knowledge}</Text>
<Text style={styles.navText}>知识库</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.navItem}>
<Text style={styles.navIcon}>{ICONS.article}</Text>
<Text style={styles.navText}>文章</Text>
</TouchableOpacity>
<TouchableOpacity style={[styles.navItem, styles.activeNavItem]}>
<Text style={styles.navIcon}>{ICONS.star}</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: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
padding: 20,
backgroundColor: '#ffffff',
borderBottomWidth: 1,
borderBottomColor: '#e2e8f0',
},
title: {
fontSize: 20,
fontWeight: 'bold',
color: '#1e293b',
},
headerActions: {
flexDirection: 'row',
alignItems: 'center',
},
searchButton: {
width: 36,
height: 36,
borderRadius: 18,
backgroundColor: '#f1f5f9',
alignItems: 'center',
justifyContent: 'center',
marginRight: 12,
},
searchIcon: {
fontSize: 18,
color: '#64748b',
},
shareButton: {
backgroundColor: '#3b82f6',
paddingHorizontal: 16,
paddingVertical: 8,
borderRadius: 20,
},
shareText: {
color: '#ffffff',
fontSize: 14,
fontWeight: '500',
},
content: {
flex: 1,
padding: 16,
},
statsContainer: {
flexDirection: 'row',
flexWrap: 'wrap',
justifyContent: 'space-between',
marginBottom: 16,
},
statCard: {
backgroundColor: '#ffffff',
borderRadius: 12,
padding: 16,
width: (width - 48) / 2,
marginBottom: 12,
elevation: 1,
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.1,
shadowRadius: 2,
flexDirection: 'row',
alignItems: 'center',
},
statIcon: {
fontSize: 24,
color: '#3b82f6',
marginRight: 12,
},
statInfo: {},
statValue: {
fontSize: 20,
fontWeight: 'bold',
color: '#1e293b',
},
statTitle: {
fontSize: 12,
color: '#64748b',
marginTop: 4,
},
sectionTitle: {
fontSize: 18,
fontWeight: 'bold',
color: '#1e293b',
marginVertical: 12,
},
categoriesContainer: {
marginBottom: 16,
},
categoriesList: {
flexDirection: 'row',
},
categoryCard: {
backgroundColor: '#ffffff',
borderRadius: 12,
padding: 12,
marginRight: 12,
alignItems: 'center',
minWidth: 80,
elevation: 1,
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.1,
shadowRadius: 2,
},
categoryIcon: {
fontSize: 24,
color: '#3b82f6',
marginBottom: 8,
},
categoryName: {
fontSize: 12,
color: '#1e293b',
textAlign: 'center',
},
categoryCount: {
fontSize: 10,
color: '#64748b',
marginTop: 4,
},
knowledgeCard: {
backgroundColor: '#ffffff',
borderRadius: 12,
padding: 16,
marginBottom: 16,
elevation: 1,
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.1,
shadowRadius: 2,
},
cardHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 12,
},
authorInfo: {
flexDirection: 'row',
alignItems: 'center',
},
authorAvatar: {
width: 32,
height: 32,
borderRadius: 16,
backgroundColor: '#dbeafe',
alignItems: 'center',
justifyContent: 'center',
marginRight: 12,
},
authorName: {
fontSize: 14,
fontWeight: 'bold',
color: '#1e293b',
},
sharedInfo: {
fontSize: 12,
color: '#64748b',
},
starIcon: {
fontSize: 20,
color: '#f59e0b',
},
cardTitle: {
fontSize: 16,
fontWeight: 'bold',
color: '#1e293b',
marginBottom: 8,
},
cardContent: {
fontSize: 14,
color: '#334155',
lineHeight: 20,
marginBottom: 12,
},
tagContainer: {
flexDirection: 'row',
flexWrap: 'wrap',
marginBottom: 12,
},
categoryTag: {
backgroundColor: '#dbeafe',
color: '#3b82f6',
fontSize: 12,
paddingHorizontal: 8,
paddingVertical: 4,
borderRadius: 12,
marginRight: 8,
marginBottom: 4,
},
tag: {
backgroundColor: '#f1f5f9',
color: '#64748b',
fontSize: 12,
paddingHorizontal: 8,
paddingVertical: 4,
borderRadius: 12,
marginRight: 8,
marginBottom: 4,
},
cardFooter: {
flexDirection: 'row',
alignItems: 'center',
borderTopWidth: 1,
borderTopColor: '#e2e8f0',
paddingTop: 12,
},
actionButton: {
flexDirection: 'row',
alignItems: 'center',
marginRight: 16,
},
actionIcon: {
fontSize: 16,
color: '#64748b',
marginRight: 4,
},
actionText: {
fontSize: 12,
color: '#64748b',
},
actionButtons: {
position: 'absolute',
right: 20,
bottom: 80,
flexDirection: 'column',
},
fabButton: {
width: 50,
height: 50,
borderRadius: 25,
backgroundColor: '#3b82f6',
alignItems: 'center',
justifyContent: 'center',
marginBottom: 12,
elevation: 3,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.25,
shadowRadius: 4,
},
fabIcon: {
fontSize: 20,
color: '#ffffff',
},
bottomNav: {
flexDirection: 'row',
justifyContent: 'space-around',
backgroundColor: '#ffffff',
borderTopWidth: 1,
borderTopColor: '#e2e8f0',
paddingVertical: 12,
},
navItem: {
alignItems: 'center',
flex: 1,
},
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 KnowledgeSharingPlatformApp

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

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

最后运行效果图如下显示:
欢迎大家加入开源鸿蒙跨平台开发者社区,一起共建开源鸿蒙跨平台生态。
更多推荐


所有评论(0)