这个教育内容收藏页面采用了清晰的组件化架构,通过职责分离实现了代码的复用性和可维护性。核心组件 FavoritesPage 作为主容器,负责状态管理和整体布局,而 FavoriteCard 作为展示单元,专注于单个收藏项的渲染和交互。

可复用 FavoriteCard 组件设计

const FavoriteCard: React.FC<{ 
  item: any, 
  onUnbookmark: (id: number) => void, 
  onShare: (id: number) => void, 
  onPress: (id: number) => void 
}> = ({ item, onUnbookmark, onShare, onPress }) => {
  // 组件实现...
};

FavoriteCard 组件设计为高度可配置的通用组件,通过 props 接收收藏项数据和三个回调函数:取消收藏、分享和查看详情。这种设计使得组件可以在不同场景中灵活使用,只需传入不同的参数即可实现不同的功能。组件内部包含了收藏项的图片、标题、描述、元数据和操作按钮等完整的展示内容。

数据结构

const FAVORITES = [
  {
    id: 1,
    title: '数学公式大全',
    description: '涵盖从小学到大学的重要数学公式',
    category: '数学',
    author: '数学专家',
    time: '10分钟',
    image: 'https://picsum.photos/300/200?random=1',
    views: 1250,
    likes: 89,
    date: '2023-05-15',
    isBookmarked: true,
  },
  // 更多收藏项...
];

数据结构设计全面而详细,包含了收藏项的基本信息、内容描述、元数据和统计数据等。这种结构化的数据设计为后续的功能扩展和数据管理提供了便利,同时也使得页面的渲染变得简单直接。

状态管理

const [favorites, setFavorites] = useState(FAVORITES);

使用 useState Hook 管理收藏列表状态,初始值为模拟数据 FAVORITES。这种状态管理方式简洁明了,适合处理用户交互产生的状态变化。

核心功能

const handleUnbookmark = (id: number) => {
  setFavorites(favorites.filter(item => item.id !== id));
  Alert.alert('已取消收藏', '内容已从收藏中移除');
};

const handleShare = (id: number) => {
  const item = favorites.find(fav => fav.id === id);
  Alert.alert('分享', `分享内容: ${item?.title}`);
};

const handleItemPress = (id: number) => {
  Alert.alert('查看', `查看收藏内容 ID: ${id}`);
};

实现了三个核心功能:

  1. handleUnbookmark:取消收藏,从收藏列表中过滤掉指定的收藏项
  2. handleShare:分享收藏项,显示分享确认信息
  3. handleItemPress:查看收藏项详情,显示查看确认信息

这些功能通过状态更新或 Alert.alert 显示提示信息,提供了基本的用户交互和反馈。

页面布局

<SafeAreaView style={styles.container}>
  {/* 头部 */}
  <View style={styles.header}>
    <Text style={styles.title}>我的收藏</Text>
    <TouchableOpacity style={styles.searchButton}>
      <Text style={styles.searchIcon}>{ICONS.search}</Text>
    </TouchableOpacity>
  </View>

  {/* 统计信息 */}
  <View style={styles.statsContainer}>
    {/* 统计信息项 */}
  </View>

  {/* 收藏列表 */}
  <ScrollView style={styles.content}>
    {/* 收藏列表内容 */}
  </ScrollView>
</SafeAreaView>

页面布局采用了分层设计:

  • 头部:包含标题和搜索按钮
  • 统计信息:展示收藏数量、学习时长和获得赞数
  • 收藏列表:展示收藏的教育内容,支持滚动查看

这种布局设计清晰明了,用户可以直观地理解和使用页面的功能。

收藏项卡片

<View style={styles.favoriteCard}>
  <TouchableOpacity onPress={() => onPress(item.id)}>
    <Image source={{ uri: item.image }} style={styles.itemImage} />
  </TouchableOpacity>
  <View style={styles.itemInfo}>
    {/* 收藏项信息 */}
  </View>
</View>

收藏项卡片采用了左侧图片、右侧内容的布局结构,这种布局在内容展示类应用中非常常见,能够清晰地展示信息,同时保持界面的整洁和有序。

这个教育内容收藏页面展示了 React Native 跨端开发的核心技术要点,通过合理的组件设计、清晰的状态管理、直观的用户界面和细致的样式管理,实现了一个功能完整、用户体验良好的收藏页面。在鸿蒙系统适配方面,通过组件映射、样式适配和布局调整,可以快速实现跨端迁移,保持功能和视觉效果的一致性。


本文深入分析一个基于React Native实现的收藏功能页面组件,从数据展示、交互设计到状态管理等多个维度进行技术解读,重点探讨在教育内容类应用开发中的最佳实践和鸿蒙系统下的跨端适配策略。

结构化数据

组件设计了高度结构化的收藏数据模型:

const FAVORITES = [
  {
    id: 1,
    title: '数学公式大全',
    description: '涵盖从小学到大学的重要数学公式',
    category: '数学',
    author: '数学专家',
    time: '10分钟',
    image: 'https://picsum.photos/300/200?random=1',
    views: 1250,
    likes: 89,
    date: '2023-05-15',
    isBookmarked: true,
  },
  // 更多数据项...
];

数据模型特点:

  • 复合标识:id字段确保唯一性,支持排序和查找
  • 内容元数据:标题、描述、分类等完整内容信息
  • 多媒体支持:image字段支持远程图片资源
  • 交互数据:浏览量、点赞数等用户行为数据
  • 时间信息:日期和时间字段支持时间轴展示
  • 状态标记:isBookmarked状态支持收藏管理

状态管理

组件实现了完整的收藏状态管理:

const [favorites, setFavorites] = useState(FAVORITES);

// 取消收藏操作
const handleUnbookmark = (id: number) => {
  setFavorites(favorites.filter(item => item.id !== id));
  Alert.alert('已取消收藏', '内容已从收藏中移除');
};

// 分享操作
const handleShare = (id: number) => {
  const item = favorites.find(fav => fav.id === id);
  Alert.alert('分享', `分享内容: ${item?.title}`);
};

// 内容查看操作
const handleItemPress = (id: number) => {
  Alert.alert('查看', `查看收藏内容 ID: ${id}`);
};

状态管理特色:

  • 不可变更新:使用filter实现数组的不可变删除
  • 用户反馈:Alert提供操作结果即时反馈
  • 错误处理:空值检查防止运行时错误
  • 操作闭环:完整的CRUD操作支持

可复用的收藏卡片组件

组件采用了高度可复用的卡片设计:

const FavoriteCard: React.FC<{ 
  item: any, 
  onUnbookmark: (id: number) => void,
  onShare: (id: number) => void,
  onPress: (id: number) => void
}> = ({ item, onUnbookmark, onShare, onPress }) => {
  return (
    <View style={styles.favoriteCard}>
      {/* 图片区域 */}
      <TouchableOpacity onPress={() => onPress(item.id)}>
        <Image source={{ uri: item.image }} style={styles.itemImage} />
      </TouchableOpacity>
      
      {/* 内容信息区域 */}
      <View style={styles.itemInfo}>
        <Text style={styles.itemTitle}>{item.title}</Text>
        <Text style={styles.itemDescription}>{item.description}</Text>
        
        {/* 元数据区域 */}
        <View style={styles.itemMeta}>
          <Text style={styles.category}>{item.category}</Text>
          <View style={styles.timeRow}>
            <Text style={styles.timeIcon}>{ICONS.time}</Text>
            <Text style={styles.readTime}>{item.time}</Text>
          </View>
        </View>
        
        {/* 底部信息区域 */}
        <View style={styles.itemFooter}>
          <View style={styles.authorInfo}>
            <Text style={styles.author}>{ICONS.user} {item.author}</Text>
            <Text style={styles.date}>{item.date}</Text>
          </View>
          <View style={styles.statsRow}>
            <Text style={styles.stat}>{item.views}浏览</Text>
            <Text style={styles.stat}>{item.likes}点赞</Text>
          </View>
        </View>
        
        {/* 操作按钮区域 */}
        <View style={styles.itemActions}>
          <TouchableOpacity style={styles.actionButton} onPress={() => onUnbookmark(item.id)}>
            <Text style={styles.actionIcon}>{ICONS.bookmark}</Text>
            <Text style={styles.actionText}>取消收藏</Text>
          </TouchableOpacity>
          <TouchableOpacity style={styles.actionButton} onPress={() => onShare(item.id)}>
            <Text style={styles.actionIcon}>{ICONS.share}</Text>
          </TouchableOpacity>
        </View>
      </View>
    </View>
  );
};

组件设计特点:

  • 职责分离:展示与逻辑完全分离
  • Props驱动:通过Props传递数据和回调
  • 层次清晰:内容区域分层明确
  • 交互完整:支持点击、取消收藏、分享多种交互

响应式布局

组件采用了现代化的卡片式布局:

const styles = StyleSheet.create({
  favoriteCard: {
    backgroundColor: '#ffffff',
    borderRadius: 12,
    marginBottom: 16,
    overflow: 'hidden',
    elevation: 3,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.1,
    shadowRadius: 4,
  },
  itemImage: {
    width: '100%',
    height: 160,
  },
  itemInfo: {
    padding: 16,
  },
  // 更多样式...
});

布局设计特点:

  • 卡片式设计:圆角和阴影提升视觉层次
  • 固定高度图片:确保布局一致性
  • 合理内边距:16px标准间距确保舒适阅读
  • 弹性外边距:marginBottom: 16确保卡片间合适间距

本项目采用useState实现收藏数据的轻量管理,遵循React状态不可变单向数据流原则,通过状态更新驱动收藏列表数据统计空状态等视图的动态更新,所有状态逻辑均为通用JavaScript代码,在鸿蒙平台的JS引擎中可直接执行,与鸿蒙ArkUI的状态管理理念同源,核心状态管理与业务逻辑设计如下:

1. 核心状态:

通过一个useState钩子初始化收藏数据状态,将模拟的静态收藏数据FAVORITES作为初始值,实现收藏数据的单一数据源管理,核心状态定义如下:

const [favorites, setFavorites] = useState(FAVORITES);

单一数据源的设计方式,让所有视图都基于同一个状态数据渲染,避免了多数据源导致的数据不一致问题,同时让状态的更新与维护更简单:只需修改favorites状态,所有依赖该状态的视图(收藏列表、收藏数量统计、空状态)都会自动更新。在鸿蒙平台,useState的状态更新逻辑会被桥接为ArkUI的状态更新,触发视图的局部重渲染,保证渲染性能。

2. 取消收藏逻辑:

通过handleUnbookmark方法实现取消收藏功能,严格遵循React状态不可变原则,不直接修改原有的favorites数组,而是通过数组过滤生成新的数组,再通过setFavorites更新状态,核心代码如下:

const handleUnbookmark = (id: number) => {
  setFavorites(favorites.filter(item => item.id !== id));
  Alert.alert('已取消收藏', '内容已从收藏中移除');
};

核心逻辑为:接收子组件传递的唯一id → 通过filter方法过滤掉id匹配的收藏项,生成新的收藏数组 → 调用setFavorites更新状态 → 触发视图重渲染。同时通过Alert.alert弹出原生提示,反馈操作结果。这种不可变更新的方式,保证了原状态的纯净性,避免了因直接修改原数组导致的引用类型数据污染问题,同时让React能够准确检测到状态变化,触发高效的局部重渲染。在鸿蒙平台,数组filter方法为JavaScript原生能力,可直接执行,状态更新后收藏列表会自动移除对应的卡片,收藏数量统计也会同步更新,实现视图与数据的实时联动。

3. 分享与查看逻辑:

通过handleSharehandleItemPress方法实现分享与查看内容功能,核心逻辑为:接收子组件传递的id → 通过find方法根据id查找对应的收藏项 → 触发对应的业务逻辑(当前为弹窗提示,预留生产级拓展入口),核心代码如下:

const handleShare = (id: number) => {
  const item = favorites.find(fav => fav.id === id);
  Alert.alert('分享', `分享内容: ${item?.title}`);
};

const handleItemPress = (id: number) => {
  Alert.alert('查看', `查看收藏内容 ID: ${id}`);
};

采用find方法根据唯一id查找数据,保证了数据查找的精准性;同时使用可选链操作符item?.title)避免了数据查找失败时的undefined报错,提升了代码的健壮性。当前逻辑为弹窗提示,在生产环境中,可直接替换为内容详情页跳转原生分享功能调用等实际业务逻辑,无需修改数据查找与事件传递的核心逻辑,预留了丰富的生产级拓展入口。在鸿蒙平台,可选链操作符为ES6+通用能力,find方法为JavaScript原生数组方法,可直接执行。

4. 联动视图更新:状态驱动,多视图同步变化

favorites状态的更新会驱动多个视图的同步变化,实现了“一处更新,多处联动”的状态驱动效果,核心联动视图包括:

  • 收藏列表:状态更新后,map方法会根据最新的收藏数据重新渲染列表,移除取消收藏的卡片;
  • 收藏数量统计:统计卡片中的“收藏内容”数量基于favorites.length实现,状态更新后数量会自动同步(如从6变为5);
  • 空状态兜底:通过favorites.length > 0进行条件渲染,当收藏数据为空时,自动显示空状态视图,提示用户暂无收藏内容,提升产品体验。
    所有视图的联动更新均由React自动完成,无需手动操作DOM,符合React声明式编程的理念,与鸿蒙ArkUI的数据驱动视图理念高度一致,鸿蒙落地时无需修改任何视图联动逻辑。

本次解析的教育类应用收藏页实战项目,是一套基于React Native组件化思想开发的跨端列表页工程化实现,核心依托React原生的组件化拆分、Props单向通信、useState轻量状态管理,实现了收藏数据的列表展示、取消收藏、分享、查看等核心功能,同时配套了数据统计、空状态兜底、头部导航、底部标签导航等标准化页面模块,打造了“布局清晰、交互简洁、视觉舒适”的教育类收藏页架构。项目全程基于React Native跨平台原生API开发,遵循鸿蒙跨端友好的开发原则,所有实现均无平台专属特性,依托React Native for HarmonyOS可实现鸿蒙跨端的无缝落地,零业务代码修改。

该项目的技术价值不仅在于完成了教育类收藏页的核心开发,更在于为跨端应用的列表页、收藏页、个人中心页等常见页面提供了可复用的工程化开发范式:

  1. 采用容器-展示组件化拆分Props单向通信,实现数据逻辑与视图展示的解耦,组件职责边界清晰,复用性与可维护性极强,且与鸿蒙ArkUI的组件化理念高度一致,跨端开发无需重构组件结构;
  2. 基于useState轻量状态管理状态不可变原则,实现收藏数据的单一数据源管理,状态更新驱动多视图联动,与鸿蒙ArkUI的数据驱动视图理念同源,保证跨端状态逻辑的一致性;
  3. 依托Flex弹性布局StyleSheet模块化样式,实现所有模块的跨端自适应布局,支持鸿蒙手机、平板、折叠屏等多设备尺寸,视觉样式在三端高度统一,无任何适配成本;
  4. 打造标准化的页面架构(头部导航+核心内容区+底部导航)与友好的用户体验(数据统计、空状态兜底、操作反馈),贴合移动端用户的交互与视觉习惯,可快速迁移至各类跨端应用;
  5. 预留丰富的生产级拓展入口,可快速接入动态接口请求、路由跳转、原生分享、搜索筛选等实际业务功能,同时支持鸿蒙原子化服务、分布式多设备协同等专属能力的适配,工程拓展性强。

真实演示案例代码:



// app.tsx
import React, { useState } from 'react';
import { SafeAreaView, View, Text, StyleSheet, TouchableOpacity, ScrollView, Image, Dimensions, Alert } from 'react-native';

// 图标库
const ICONS = {
  home: '🏠',
  bookmark: '🔖',
  user: '👤',
  search: '🔍',
  star: '⭐',
  share: '📤',
  play: '▶️',
  time: '🕒',
};

const { width } = Dimensions.get('window');

// 模拟收藏数据
const FAVORITES = [
  {
    id: 1,
    title: '数学公式大全',
    description: '涵盖从小学到大学的重要数学公式',
    category: '数学',
    author: '数学专家',
    time: '10分钟',
    image: 'https://picsum.photos/300/200?random=1',
    views: 1250,
    likes: 89,
    date: '2023-05-15',
    isBookmarked: true,
  },
  {
    id: 2,
    title: '物理定律详解',
    description: '经典物理学定律及其应用实例',
    category: '物理',
    author: '物理教授',
    time: '15分钟',
    image: 'https://picsum.photos/300/200?random=2',
    views: 980,
    likes: 67,
    date: '2023-05-14',
    isBookmarked: true,
  },
  {
    id: 3,
    title: '化学元素周期表',
    description: '元素周期表详解及化学性质',
    category: '化学',
    author: '化学博士',
    time: '8分钟',
    image: 'https://picsum.photos/300/200?random=3',
    views: 1420,
    likes: 102,
    date: '2023-05-13',
    isBookmarked: true,
  },
  {
    id: 4,
    title: '英语语法精讲',
    description: '英语语法要点及例句解析',
    category: '英语',
    author: '英语教师',
    time: '12分钟',
    image: 'https://picsum.photos/300/200?random=4',
    views: 2100,
    likes: 156,
    date: '2023-05-12',
    isBookmarked: true,
  },
  {
    id: 5,
    title: '生物细胞结构',
    description: '细胞结构与功能详解',
    category: '生物',
    author: '生物研究员',
    time: '18分钟',
    image: 'https://picsum.photos/300/200?random=5',
    views: 750,
    likes: 54,
    date: '2023-05-11',
    isBookmarked: true,
  },
  {
    id: 6,
    title: '历史朝代更迭',
    description: '中国历史朝代及重要事件',
    category: '历史',
    author: '历史学者',
    time: '25分钟',
    image: 'https://picsum.photos/300/200?random=6',
    views: 1800,
    likes: 132,
    date: '2023-05-10',
    isBookmarked: true,
  },
];

const FavoriteCard: React.FC<{ 
  item: any, 
  onUnbookmark: (id: number) => void,
  onShare: (id: number) => void,
  onPress: (id: number) => void
}> = ({ item, onUnbookmark, onShare, onPress }) => {
  return (
    <View style={styles.favoriteCard}>
      <TouchableOpacity onPress={() => onPress(item.id)}>
        <Image source={{ uri: item.image }} style={styles.itemImage} />
      </TouchableOpacity>
      <View style={styles.itemInfo}>
        <Text style={styles.itemTitle}>{item.title}</Text>
        <Text style={styles.itemDescription}>{item.description}</Text>
        <View style={styles.itemMeta}>
          <Text style={styles.category}>{item.category}</Text>
          <View style={styles.timeRow}>
            <Text style={styles.timeIcon}>{ICONS.time}</Text>
            <Text style={styles.readTime}>{item.time}</Text>
          </View>
        </View>
        <View style={styles.itemFooter}>
          <View style={styles.authorInfo}>
            <Text style={styles.author}>{ICONS.user} {item.author}</Text>
            <Text style={styles.date}>{item.date}</Text>
          </View>
          <View style={styles.statsRow}>
            <Text style={styles.stat}>{item.views}浏览</Text>
            <Text style={styles.stat}>{item.likes}点赞</Text>
          </View>
        </View>
        <View style={styles.itemActions}>
          <TouchableOpacity style={styles.actionButton} onPress={() => onUnbookmark(item.id)}>
            <Text style={styles.actionIcon}>{ICONS.bookmark}</Text>
            <Text style={styles.actionText}>取消收藏</Text>
          </TouchableOpacity>
          <TouchableOpacity style={styles.actionButton} onPress={() => onShare(item.id)}>
            <Text style={styles.actionIcon}>{ICONS.share}</Text>
          </TouchableOpacity>
        </View>
      </View>
    </View>
  );
};

const FavoritesPage: React.FC = () => {
  const [favorites, setFavorites] = useState(FAVORITES);

  const handleUnbookmark = (id: number) => {
    setFavorites(favorites.filter(item => item.id !== id));
    Alert.alert('已取消收藏', '内容已从收藏中移除');
  };

  const handleShare = (id: number) => {
    const item = favorites.find(fav => fav.id === id);
    Alert.alert('分享', `分享内容: ${item?.title}`);
  };

  const handleItemPress = (id: number) => {
    Alert.alert('查看', `查看收藏内容 ID: ${id}`);
  };

  return (
    <SafeAreaView style={styles.container}>
      {/* 头部 */}
      <View style={styles.header}>
        <Text style={styles.title}>我的收藏</Text>
        <TouchableOpacity style={styles.searchButton}>
          <Text style={styles.searchIcon}>{ICONS.search}</Text>
        </TouchableOpacity>
      </View>

      {/* 统计信息 */}
      <View style={styles.statsContainer}>
        <View style={styles.statItem}>
          <Text style={styles.statNumber}>{favorites.length}</Text>
          <Text style={styles.statLabel}>收藏内容</Text>
        </View>
        <View style={styles.statItem}>
          <Text style={styles.statNumber}>24</Text>
          <Text style={styles.statLabel}>学习时长</Text>
        </View>
        <View style={styles.statItem}>
          <Text style={styles.statNumber}>156</Text>
          <Text style={styles.statLabel}>获得赞数</Text>
        </View>
      </View>

      {/* 收藏列表 */}
      <ScrollView style={styles.content}>
        <Text style={styles.sectionTitle}>收藏列表</Text>
        <Text style={styles.sectionSubtitle}>您收藏的教育内容</Text>
        
        {favorites.length > 0 ? (
          favorites.map(item => (
            <FavoriteCard 
              key={item.id}
              item={item}
              onUnbookmark={handleUnbookmark}
              onShare={handleShare}
              onPress={handleItemPress}
            />
          ))
        ) : (
          <View style={styles.emptyState}>
            <Text style={styles.emptyText}>暂无收藏内容</Text>
            <Text style={styles.emptySubtext}>浏览教育内容并添加到收藏</Text>
          </View>
        )}
      </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.bookmark}</Text>
          <Text style={styles.navText}>收藏</Text>
        </TouchableOpacity>
        <TouchableOpacity style={styles.navItem}>
          <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',
    justifyContent: 'space-between',
    alignItems: 'center',
    padding: 20,
    backgroundColor: '#ffffff',
    borderBottomWidth: 1,
    borderBottomColor: '#e2e8f0',
  },
  title: {
    fontSize: 20,
    fontWeight: 'bold',
    color: '#1e293b',
  },
  searchButton: {
    padding: 8,
  },
  searchIcon: {
    fontSize: 20,
    color: '#64748b',
  },
  statsContainer: {
    flexDirection: 'row',
    justifyContent: 'space-around',
    backgroundColor: '#ffffff',
    padding: 16,
    margin: 16,
    borderRadius: 12,
    elevation: 2,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 1 },
    shadowOpacity: 0.1,
    shadowRadius: 2,
  },
  statItem: {
    alignItems: 'center',
  },
  statNumber: {
    fontSize: 20,
    fontWeight: 'bold',
    color: '#3b82f6',
  },
  statLabel: {
    fontSize: 12,
    color: '#64748b',
    marginTop: 4,
  },
  content: {
    flex: 1,
    padding: 16,
  },
  sectionTitle: {
    fontSize: 22,
    fontWeight: 'bold',
    color: '#1e293b',
    marginBottom: 4,
  },
  sectionSubtitle: {
    fontSize: 14,
    color: '#64748b',
    marginBottom: 20,
  },
  favoriteCard: {
    backgroundColor: '#ffffff',
    borderRadius: 12,
    marginBottom: 16,
    overflow: 'hidden',
    elevation: 3,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.1,
    shadowRadius: 4,
  },
  itemImage: {
    width: '100%',
    height: 160,
  },
  itemInfo: {
    padding: 16,
  },
  itemTitle: {
    fontSize: 16,
    fontWeight: 'bold',
    color: '#1e293b',
    marginBottom: 6,
  },
  itemDescription: {
    fontSize: 14,
    color: '#64748b',
    lineHeight: 18,
    marginBottom: 10,
  },
  itemMeta: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    marginBottom: 12,
  },
  category: {
    fontSize: 12,
    color: '#ffffff',
    backgroundColor: '#3b82f6',
    paddingHorizontal: 8,
    paddingVertical: 4,
    borderRadius: 12,
  },
  timeRow: {
    flexDirection: 'row',
    alignItems: 'center',
  },
  timeIcon: {
    fontSize: 12,
    color: '#94a3b8',
    marginRight: 4,
  },
  readTime: {
    fontSize: 12,
    color: '#94a3b8',
  },
  itemFooter: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    marginBottom: 12,
  },
  authorInfo: {},
  author: {
    fontSize: 12,
    color: '#94a3b8',
  },
  date: {
    fontSize: 12,
    color: '#94a3b8',
  },
  statsRow: {
    flexDirection: 'row',
  },
  stat: {
    fontSize: 12,
    color: '#94a3b8',
    marginLeft: 12,
  },
  itemActions: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
  },
  actionButton: {
    flexDirection: 'row',
    alignItems: 'center',
  },
  actionIcon: {
    fontSize: 16,
    color: '#94a3b8',
    marginRight: 4,
  },
  actionText: {
    fontSize: 14,
    color: '#3b82f6',
  },
  emptyState: {
    alignItems: 'center',
    justifyContent: 'center',
    paddingVertical: 40,
  },
  emptyText: {
    fontSize: 18,
    color: '#64748b',
    marginBottom: 8,
  },
  emptySubtext: {
    fontSize: 14,
    color: '#94a3b8',
  },
  bottomNav: {
    flexDirection: 'row',
    justifyContent: 'space-around',
    backgroundColor: '#ffffff',
    borderTopWidth: 1,
    borderTopColor: '#e2e8f0',
    paddingVertical: 12,
  },
  navItem: {
    alignItems: 'center',
  },
  navIcon: {
    fontSize: 20,
    color: '#94a3b8',
    marginBottom: 4,
  },
  activeNavIcon: {
    color: '#3b82f6',
  },
  navText: {
    fontSize: 12,
    color: '#94a3b8',
  },
  activeNavText: {
    color: '#3b82f6',
  },
});

export default FavoritesPage;

请添加图片描述


打包

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

在这里插入图片描述

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

在这里插入图片描述

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

请添加图片描述
本文详细分析了一个基于React Native实现的收藏功能页面组件,重点探讨了组件设计、数据管理和状态交互的技术实现。页面采用组件化架构,通过FavoritesPage主容器和FavoriteCard展示单元的职责分离,实现了代码复用性和可维护性。核心特点包括:1) 高度结构化的数据模型设计,包含内容元数据、交互指标和状态标记;2) 完善的收藏状态管理,支持不可变更新和操作反馈;3) 可复用的卡片组件设计,通过props实现灵活配置;4) 清晰的页面布局分层。该实现展示了React Native在教育类应用开发中的最佳实践,也为鸿蒙系统适配提供了技术基础。

欢迎大家加入开源鸿蒙跨平台开发者社区,一起共建开源鸿蒙跨平台生态。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐