在移动应用开发中,新闻应用是一种常见的应用类型,需要考虑内容展示、用户交互、数据持久化等多个方面。本文将深入分析一个功能完备的 React Native 新闻应用实现,探讨其架构设计、状态管理、数据持久化以及跨端兼容性策略。

组件化架

该实现采用了清晰的组件化架构,主要包含以下部分:

  • 主应用组件 (NewsApp) - 负责整体布局和状态管理
  • 新闻列表渲染 - 负责渲染新闻卡片列表
  • 功能按钮 - 提供点赞、评论、分享、收藏等功能
  • 标签页导航 - 实现新闻、收藏、历史记录的切换

这种架构设计使得代码结构清晰,易于维护和扩展。主应用组件负责管理全局状态和业务逻辑,而各个功能部分负责具体的 UI 渲染,实现了关注点分离。

状态管理

NewsApp 组件使用 useState 钩子管理多个关键状态:

const [newsItems, setNewsItems] = useState<NewsItem[]>(initialNewsItems);
const [favoriteNews, setFavoriteNews] = useState<string[]>([]);
const [readHistory, setReadHistory] = useState<string[]>([]);
const [activeTab, setActiveTab] = useState<'news' | 'favorites' | 'history'>('news');

这种状态管理方式简洁高效,通过状态更新触发组件重新渲染,实现了新闻的展示、点赞、收藏等功能。使用 TypeScript 类型定义确保了数据结构的类型安全,提高了代码的可靠性。


本地存储

应用集成了 AsyncStorage 实现数据持久化:

// 加载本地存储的数据
useEffect(() => {
  loadStoredData();
}, []);

const loadStoredData = async () => {
  try {
    const storedNews = await AsyncStorage.getItem(STORAGE_KEYS.NEWS_ITEMS);
    if (storedNews) {
      setNewsItems(JSON.parse(storedNews));
    }

    const storedFavorites = await AsyncStorage.getItem(STORAGE_KEYS.FAVORITE_NEWS);
    if (storedFavorites) {
      setFavoriteNews(JSON.parse(storedFavorites));
    }

    const storedHistory = await AsyncStorage.getItem(STORAGE_KEYS.READ_HISTORY);
    if (storedHistory) {
      setReadHistory(JSON.parse(storedHistory));
    }
  } catch (error) {
    console.error('加载本地数据失败:', error);
  }
};

// 保存数据到本地存储
const saveToStorage = async (key: string, data: any) => {
  try {
    await AsyncStorage.setItem(key, JSON.stringify(data));
  } catch (error) {
    console.error('保存数据失败:', error);
  }
};

这种实现方式确保了应用数据在应用重启后不会丢失,提高了用户体验。通过 useEffect 钩子在组件挂载时加载数据,确保了应用启动时能够显示之前的状态。

数据同步

应用实现了数据同步机制,当用户进行点赞、收藏等操作时,会同时更新内存中的状态和本地存储:

const handleLike = (id: string) => {
  const updatedNews = newsItems.map(item =>
    item.id === id
      ? { ...item, likes: item.isLiked ? item.likes - 1 : item.likes + 1, isLiked: !item.isLiked }
      : item
  );
  setNewsItems(updatedNews);
  saveToStorage(STORAGE_KEYS.NEWS_ITEMS, updatedNews);
};

这种实现方式确保了内存状态和本地存储的一致性,避免了数据不同步的问题。


新闻管理功能

应用实现了完整的新闻管理功能:

  • 新闻列表展示 - 显示新闻标题、摘要、作者、发布时间等信息
  • 标签页切换 - 在新闻、收藏、历史记录之间切换
  • 点赞功能 - 允许用户点赞新闻,更新点赞数
  • 收藏功能 - 允许用户收藏新闻,方便以后查看
  • 阅读历史 - 记录用户阅读过的新闻

这些功能覆盖了新闻应用的基本需求,为用户提供了便捷的新闻浏览体验。

交互设计

应用实现了直观的交互设计:

  • 点赞 - 点击点赞按钮切换点赞状态
  • 收藏 - 点击收藏按钮切换收藏状态
  • 评论 - 点击评论按钮查看或添加评论
  • 分享 - 点击分享按钮分享新闻
  • 标签页切换 - 点击标签页切换不同的内容视图

这些交互设计元素共同构成了良好的用户体验,使得新闻浏览和交互操作简单直观。


类型定义

该实现使用 TypeScript 定义了两个核心数据类型:

  1. NewsItem - 新闻类型,包含新闻的完整信息,如标题、摘要、内容、作者、发布时间、分类、阅读时间、点赞数、评论数、分享数、点赞状态、收藏状态和突发新闻标记
  2. Category - 分类类型,包含分类的 ID、名称和图标

这些类型定义使得数据结构更加清晰,提高了代码的可读性和可维护性,同时也提供了类型安全保障。

数据

应用数据按照功能模块进行组织:

  • newsItems - 新闻列表
  • favoriteNews - 收藏的新闻 ID 列表
  • readHistory - 阅读历史的新闻 ID 列表
  • activeTab - 当前激活的标签页

这种数据组织方式使得数据管理更加清晰,易于扩展和维护。


渲染

  1. 列表优化 - 使用 FlatList 渲染新闻列表,提高长列表的渲染性能
  2. 条件渲染 - 只在需要时渲染特定的 UI 元素,减少不必要的渲染
  3. 组件拆分 - 将新闻卡片和功能按钮等拆分为独立的组件,提高渲染性能
  4. 样式复用 - 通过样式数组和条件样式,复用样式定义,减少样式计算开销

状态管理

  1. 批量状态更新 - 避免频繁的状态更新,尽量批量更新状态
  2. 不可变数据 - 使用不可变数据模式更新状态,确保状态更新的可预测性
  3. 状态清理 - 在组件卸载时清理不必要的状态,减少内存使用

存储

  1. 按需加载 - 只在需要时加载本地存储的数据,避免启动时加载过多数据
  2. 增量更新 - 只更新变化的数据,避免每次都保存完整的数据集
  3. 错误处理 - 对存储操作进行错误处理,确保应用的稳定性

配置化

该实现采用了高度配置化的设计,通过数据结构可以灵活配置应用的行为:

  • 新闻数据 - 通过修改 initialNewsItems 数组,可以轻松添加或修改新闻
  • 本地存储 - 通过修改 STORAGE_KEYS 对象,可以灵活配置本地存储的键
  • 标签页 - 通过修改标签页相关的状态和渲染逻辑,可以轻松添加或修改标签页

这种设计使得应用能够轻松适应不同的使用场景,无需修改核心代码结构。


1. FlatList 渲染新闻列表

当前实现使用 FlatList 渲染新闻列表,这是一个好的做法,但可以进一步优化:

// 优化前
<FlatList
  data={newsItems}
  renderItem={({ item }) => (
    <NewsCard news={item} />
  )}
  keyExtractor={item => item.id}
/>

// 优化后
<FlatList
  data={newsItems}
  renderItem={({ item }) => (
    <NewsCard news={item} />
  )}
  keyExtractor={item => item.id}
  initialNumToRender={5} // 初始渲染的项目数
  maxToRenderPerBatch={10} // 每批渲染的最大项目数
  windowSize={10} // 可见区域外渲染的项目数
  removeClippedSubviews={true} // 移除不可见的子视图
  updateCellsBatchingPeriod={100} // 单元格更新的批处理周期
  getItemLayout={(data, index) => ({
    length: ITEM_HEIGHT, // 预计算的项目高度
    offset: ITEM_HEIGHT * index,
    index
  })}
/>

2. 状态管理

当前实现使用多个 useState 钩子管理状态,可以考虑使用 useReducer 或状态管理库来管理复杂状态:

// 优化前
const [newsItems, setNewsItems] = useState<NewsItem[]>(initialNewsItems);
const [favoriteNews, setFavoriteNews] = useState<string[]>([]);
const [readHistory, setReadHistory] = useState<string[]>([]);
const [activeTab, setActiveTab] = useState<'news' | 'favorites' | 'history'>('news');

// 优化后
type AppState = {
  newsItems: NewsItem[];
  favoriteNews: string[];
  readHistory: string[];
  activeTab: 'news' | 'favorites' | 'history';
};

type AppAction =
  | { type: 'SET_NEWS_ITEMS'; payload: NewsItem[] }
  | { type: 'SET_FAVORITE_NEWS'; payload: string[] }
  | { type: 'SET_READ_HISTORY'; payload: string[] }
  | { type: 'SET_ACTIVE_TAB'; payload: 'news' | 'favorites' | 'history' }
  | { type: 'TOGGLE_LIKE'; payload: string }
  | { type: 'TOGGLE_BOOKMARK'; payload: string }
  | { type: 'ADD_TO_HISTORY'; payload: string };

const initialState: AppState = {
  newsItems: initialNewsItems,
  favoriteNews: [],
  readHistory: [],
  activeTab: 'news'
};

const appReducer = (state: AppState, action: AppAction): AppState => {
  switch (action.type) {
    case 'SET_NEWS_ITEMS':
      return { ...state, newsItems: action.payload };
    case 'SET_FAVORITE_NEWS':
      return { ...state, favoriteNews: action.payload };
    case 'SET_READ_HISTORY':
      return { ...state, readHistory: action.payload };
    case 'SET_ACTIVE_TAB':
      return { ...state, activeTab: action.payload };
    case 'TOGGLE_LIKE':
      return {
        ...state,
        newsItems: state.newsItems.map(item =>
          item.id === action.payload
            ? { ...item, likes: item.isLiked ? item.likes - 1 : item.likes + 1, isLiked: !item.isLiked }
            : item
        )
      };
    case 'TOGGLE_BOOKMARK':
      return {
        ...state,
        newsItems: state.newsItems.map(item =>
          item.id === action.payload
            ? { ...item, isBookmarked: !item.isBookmarked }
            : item
        ),
        favoriteNews: state.newsItems.find(item => item.id === action.payload)?.isBookmarked
          ? state.favoriteNews.filter(id => id !== action.payload)
          : [...state.favoriteNews, action.payload]
      };
    case 'ADD_TO_HISTORY':
      return {
        ...state,
        readHistory: [action.payload, ...state.readHistory.filter(id => id !== action.payload)].slice(0, 50)
      };
    default:
      return state;
  }
};

const [state, dispatch] = useReducer(appReducer, initialState);

3. 网络请求

当前实现使用静态数据,可以考虑集成网络请求获取实时新闻:

import axios from 'axios';

const fetchNews = async () => {
  try {
    const response = await axios.get('https://api.example.com/news');
    const news = response.data;
    setNewsItems(news);
    saveToStorage(STORAGE_KEYS.NEWS_ITEMS, news);
  } catch (error) {
    console.error('获取新闻失败:', error);
    // 使用本地存储的缓存数据
  }
};

useEffect(() => {
  fetchNews();
  const interval = setInterval(fetchNews, 60000); // 每分钟刷新一次
  
  return () => clearInterval(interval);
}, []);

4. 导航系统

可以集成 React Navigation 实现新闻详情页面的导航:

import { createStackNavigator } from '@react-navigation/stack';

const Stack = createStackNavigator();

const App = () => {
  return (
    <NavigationContainer>
      <Stack.Navigator>
        <Stack.Screen 
          name="NewsList" 
          component={NewsApp} 
          options={{ title: '新闻' }} 
        />
        <Stack.Screen 
          name="NewsDetail" 
          component={NewsDetailScreen} 
          options={({ route }) => ({ title: route.params?.newsTitle || '新闻详情' })} 
        />
      </Stack.Navigator>
    </NavigationContainer>
  );
};

const NewsDetailScreen = ({ route }: { route: any }) => {
  const { newsId } = route.params;
  // 获取新闻详情并渲染
  return (
    <View style={styles.detailContainer}>
      {/* 新闻详情内容 */}
    </View>
  );
};

本文深入分析了一个功能完备的 React Native 新闻应用实现,从架构设计、状态管理、数据持久化到跨端兼容性都进行了详细探讨。该实现不仅功能完整,而且代码结构清晰,具有良好的可扩展性和可维护性。


理解这个功能完整的 React Native 新闻资讯应用的技术实现细节,同时掌握其向鸿蒙(HarmonyOS)平台跨端适配的核心思路与实践方案。该应用具备新闻展示、收藏、阅读历史、本地存储等完整功能,是典型的内容类应用架构,涵盖了移动端开发的核心场景与技术要点。

1. 应用架构

该新闻资讯应用采用分层设计+状态驱动的现代化 React Native 架构,核心数据模型设计体现了内容类应用的典型特征:

// 新闻核心数据模型 - 覆盖资讯类应用全维度属性
type NewsItem = {
  id: string;                // 唯一标识,用于CRUD操作
  title: string;             // 新闻标题
  summary: string;           // 新闻摘要
  content: string;           // 完整内容
  author: string;            // 作者
  publishTime: string;       // 发布时间
  category: string;          // 分类
  readTime: string;          // 预估阅读时长
  likes: number;             // 点赞数
  comments: number;          // 评论数
  shares: number;            // 分享数
  isLiked: boolean;          // 是否已点赞(用户交互状态)
  isBookmarked: boolean;     // 是否已收藏
  isBreaking: boolean;       // 是否热点新闻
};

// 分类数据模型
type Category = {
  id: string;                // 分类ID
  name: string;              // 分类名称
  icon: string;              // 分类图标
};

// 本地存储键常量 - 统一管理存储标识
const STORAGE_KEYS = {
  NEWS_ITEMS: '@news_items',
  FAVORITE_NEWS: '@favorite_news',
  READ_HISTORY: '@read_history',
};

数据模型设计亮点

  • 业务完整性:包含新闻资讯所需的全部核心字段,兼顾内容展示与用户交互;
  • 状态标识清晰:通过布尔值标识用户交互状态(点赞/收藏),简化状态管理;
  • 业务特征明显isBreaking 标识热点新闻,readTime 提供阅读体验预估;
  • 存储规范化:通过常量管理本地存储键名,避免硬编码错误。

2. 状态管理

应用通过 React Hooks 实现精细化的状态管理,多状态协同完成新闻内容的全生命周期管理:

// 核心状态定义
const [newsItems, setNewsItems] = useState<NewsItem[]>(initialNewsItems); // 新闻列表数据源
const [favoriteNews, setFavoriteNews] = useState<string[]>([]);          // 收藏新闻ID列表
const [readHistory, setReadHistory] = useState<string[]>([]);            // 阅读历史ID列表
const [activeTab, setActiveTab] = useState<'news' | 'favorites' | 'history'>('news'); // 标签页状态
(1)本地存储管理
// 组件挂载时加载本地数据
useEffect(() => {
  loadStoredData();
}, []);

// 加载本地存储数据
const loadStoredData = async () => {
  try {
    const storedNews = await AsyncStorage.getItem(STORAGE_KEYS.NEWS_ITEMS);
    if (storedNews) setNewsItems(JSON.parse(storedNews));

    const storedFavorites = await AsyncStorage.getItem(STORAGE_KEYS.FAVORITE_NEWS);
    if (storedFavorites) setFavoriteNews(JSON.parse(storedFavorites));

    const storedHistory = await AsyncStorage.getItem(STORAGE_KEYS.READ_HISTORY);
    if (storedHistory) setReadHistory(JSON.parse(storedHistory));
  } catch (error) {
    console.error('加载本地数据失败:', error);
  }
};

// 通用存储方法封装
const saveToStorage = async (key: string, data: any) => {
  try {
    await AsyncStorage.setItem(key, JSON.stringify(data));
  } catch (error) {
    console.error('保存数据失败:', error);
  }
};
(2)点赞交互逻辑(含状态同步)
const handleLike = (id: string) => {
  // 不可变更新新闻状态
  const updatedNews = newsItems.map(item => 
    item.id === id 
      ? { ...item, likes: item.isLiked ? item.likes - 1 : item.likes + 1, isLiked: !item.isLiked } 
      : item
  );
  setNewsItems(updatedNews);
  saveToStorage(STORAGE_KEYS.NEWS_ITEMS, updatedNews); // 同步到本地存储
};
(3)收藏管理
const handleBookmark = (id: string) => {
  // 更新新闻收藏状态
  const updatedNews = newsItems.map(item => 
    item.id === id 
      ? { ...item, isBookmarked: !item.isBookmarked } 
      : item
  );
  setNewsItems(updatedNews);
  saveToStorage(STORAGE_KEYS.NEWS_ITEMS, updatedNews);

  // 同步更新收藏列表
  const updatedFavorites = [...favoriteNews];
  if (updatedFavorites.includes(id)) {
    const index = updatedFavorites.indexOf(id);
    updatedFavorites.splice(index, 1); // 取消收藏
  } else {
    updatedFavorites.push(id); // 添加收藏
  }
  setFavoriteNews(updatedFavorites);
  saveToStorage(STORAGE_KEYS.FAVORITE_NEWS, updatedFavorites);
};
(4)阅读历史管理
const handleReadMore = (news: NewsItem) => {
  // 更新阅读历史,保持最多20条记录
  const updatedHistory = [news.id, ...readHistory.filter(id => id !== news.id)].slice(0, 20);
  setReadHistory(updatedHistory);
  saveToStorage(STORAGE_KEYS.READ_HISTORY, updatedHistory);

  Alert.alert('阅读新闻', `正在阅读: ${news.title}\n\n${news.content}`);
};

应用采用组件化拆分思想,将UI拆分为多个可复用组件,提升代码可维护性:

(1)新闻卡片组件

核心展示组件,封装了新闻内容展示、交互按钮等完整功能:

const NewsCard = ({ 
  news, 
  onLike,
  onBookmark,
  onReadMore
}: { 
  news: NewsItem; 
  onLike: (id: string) => void;
  onBookmark: (id: string) => void;
  onReadMore: (news: NewsItem) => void;
}) => {
  return (
    <View style={styles.newsCard}>
      {/* 分类标签与热点标识 */}
      <View style={styles.newsHeader}>
        <View style={styles.categoryBadge}>
          <Text style={styles.categoryText}>{news.category}</Text>
        </View>
        {news.isBreaking && (
          <View style={styles.breakingBadge}>
            <Text style={styles.breakingText}>🔥 热点</Text>
          </View>
        )}
      </View>
      
      {/* 新闻标题与摘要 */}
      <TouchableOpacity onPress={() => onReadMore(news)}>
        <Text style={styles.newsTitle}>{news.title}</Text>
        <Text style={styles.newsSummary} numberOfLines={2}>{news.summary}</Text>
      </TouchableOpacity>
      
      {/* 元信息 */}
      <View style={styles.newsMeta}>
        {/* 作者、时间、阅读时长 */}
      </View>
      
      {/* 交互按钮 */}
      <View style={styles.newsActions}>
        <TouchableOpacity style={styles.actionButton} onPress={() => onLike(news.id)}>
          <Text style={[styles.actionIcon, news.isLiked && styles.likedIcon]}>👍</Text>
          <Text style={styles.actionText}>{news.likes}</Text>
        </TouchableOpacity>
        {/* 评论、收藏、分享按钮 */}
      </View>
    </View>
  );
};
(2)列表渲染优化

使用 FlatList 替代基础 ScrollView 实现长列表渲染,提升性能:

<FlatList
  data={newsItems}
  keyExtractor={item => item.id}
  renderItem={({ item }) => (
    <NewsCard
      news={item}
      onLike={handleLike}
      onBookmark={handleBookmark}
      onReadMore={handleReadMore}
    />
  )}
  showsVerticalScrollIndicator={false}
/>
(3)标签页切换

通过 activeTab 状态实现内容区域的条件渲染:

{activeTab === 'news' && (/* 最新新闻内容 */)}
{activeTab === 'favorites' && (/* 收藏内容 */)}
{activeTab === 'history' && (/* 阅读历史 */)}

应用采用 StyleSheet 实现样式与逻辑分离,遵循移动端设计规范:

  • 卡片式设计:所有内容模块使用圆角卡片,提升视觉层次感;
  • 状态视觉反馈:点赞/收藏按钮有明确的选中态样式;
  • 响应式布局:使用 Dimensions 获取屏幕尺寸,适配不同设备;
  • 滚动优化:水平滚动容器隐藏滚动条,提升用户体验;
  • 空状态处理:收藏/历史页面提供友好的空状态提示。

将该 React Native 新闻应用适配到鸿蒙平台,核心是将 React 的状态管理、组件体系、存储机制映射到鸿蒙 ArkTS + ArkUI 生态,以下是完整的适配方案。

1. 核心技术栈映射

React Native 核心能力 鸿蒙 ArkTS 对应实现 适配关键说明
useState 状态管理 @State/@Link/@StorageProp 状态声明语法替换,逻辑一致
useEffect 生命周期 onPageShow/aboutToAppear 生命周期方法替换
AsyncStorage 本地存储 Preferences 数据存储 存储API替换,数据格式兼容
FlatList 列表渲染 List + LazyForEach 虚拟列表渲染,性能更优
ScrollView 滚动容器 Scroll 组件 滚动容器替换
TouchableOpacity Button + stateEffect(false) 可点击组件替换
StyleSheet.create @Styles/@Extend + 内联样式 样式体系重构
Alert.alert AlertDialog 组件 弹窗交互替换
条件渲染(三元运算符) if/else 条件渲染 视图控制语法适配
不可变状态更新 直接状态赋值 + 强制更新 状态更新机制调整

2. 鸿蒙端

// index.ets - 鸿蒙端入口文件
import router from '@ohos.router';
import preferences from '@ohos.data.preferences';

@Entry
@Component
struct NewsApp {
  // 核心状态定义(对应 React Native 的 useState)
  @State newsItems: NewsItem[] = [
    {
      id: '1',
      title: '全球气候变化峰会达成历史性协议',
      summary: '各国领导人就减排目标达成一致,承诺在未来十年内大幅减少碳排放。这一协议被认为是对抗气候变化的重要里程碑。',
      content: '全球气候变化峰会在经过两周的紧张谈判后,各国代表终于达成了一项历史性的协议。根据协议内容,所有签署国承诺在未来十年内将其温室气体排放量减少50%,并投入大量资金用于绿色能源的研发和推广。专家认为,这项协议将对全球气候产生深远影响。',
      author: '张记者',
      publishTime: '2小时前',
      category: '国际',
      readTime: '5',
      likes: 245,
      comments: 42,
      shares: 18,
      isLiked: false,
      isBookmarked: true,
      isBreaking: true
    },
    // 其他初始化数据...
  ];

  @State favoriteNews: string[] = [];
  @State readHistory: string[] = [];
  @State activeTab: 'news' | 'favorites' | 'history' = 'news';
  
  // 存储管理器实例
  private prefManager: preferences.Preferences | null = null;

  // 类型定义(完全复用 React Native 的类型)
  type NewsItem = {
    id: string;
    title: string;
    summary: string;
    content: string;
    author: string;
    publishTime: string;
    category: string;
    readTime: string;
    likes: number;
    comments: number;
    shares: number;
    isLiked: boolean;
    isBookmarked: boolean;
    isBreaking: boolean;
  };

  type Category = {
    id: string;
    name: string;
    icon: string;
  };

  // 本地存储键(复用 React Native 定义)
  private STORAGE_KEYS = {
    NEWS_ITEMS: 'news_items',
    FAVORITE_NEWS: 'favorite_news',
    READ_HISTORY: 'read_history',
  };

  // 通用样式封装 - 替代 React Native 的 StyleSheet
  @Styles
  cardShadow() {
    .shadow({ radius: 2, color: '#000', opacity: 0.1, offsetX: 0, offsetY: 1 });
  }

  // 生命周期方法(对应 React 的 useEffect)
  async aboutToAppear() {
    await this.initPreferences();
    await this.loadStoredData();
  }

  // 初始化偏好存储
  private async initPreferences() {
    try {
      this.prefManager = await preferences.getPreferences(getContext(), 'news_app_prefs');
    } catch (error) {
      console.error('初始化存储失败:', error);
    }
  }

  // 加载本地存储数据(对应 React Native 的 loadStoredData)
  private async loadStoredData() {
    if (!this.prefManager) return;

    try {
      // 读取新闻数据
      const storedNews = await this.prefManager.get(this.STORAGE_KEYS.NEWS_ITEMS, '');
      if (storedNews !== '') {
        this.newsItems = JSON.parse(storedNews as string);
      }

      // 读取收藏数据
      const storedFavorites = await this.prefManager.get(this.STORAGE_KEYS.FAVORITE_NEWS, '');
      if (storedFavorites !== '') {
        this.favoriteNews = JSON.parse(storedFavorites as string);
      }

      // 读取阅读历史
      const storedHistory = await this.prefManager.get(this.STORAGE_KEYS.READ_HISTORY, '');
      if (storedHistory !== '') {
        this.readHistory = JSON.parse(storedHistory as string);
      }
    } catch (error) {
      console.error('加载本地数据失败:', error);
    }
  }

  // 保存数据到本地存储(对应 React Native 的 saveToStorage)
  private async saveToStorage(key: string, data: any) {
    if (!this.prefManager) return;

    try {
      await this.prefManager.put(key, JSON.stringify(data));
      await this.prefManager.flush(); // 立即刷盘
    } catch (error) {
      console.error('保存数据失败:', error);
    }
  }

  // 点赞处理(核心逻辑复用)
  private handleLike(id: string) {
    this.newsItems = this.newsItems.map(item => 
      item.id === id 
        ? { ...item, likes: item.isLiked ? item.likes - 1 : item.likes + 1, isLiked: !item.isLiked } 
        : item
    );
    this.saveToStorage(this.STORAGE_KEYS.NEWS_ITEMS, this.newsItems);
  }

  // 收藏处理(核心逻辑复用)
  private handleBookmark(id: string) {
    // 更新新闻收藏状态
    this.newsItems = this.newsItems.map(item => 
      item.id === id 
        ? { ...item, isBookmarked: !item.isBookmarked } 
        : item
    );
    this.saveToStorage(this.STORAGE_KEYS.NEWS_ITEMS, this.newsItems);

    // 更新收藏列表
    const updatedFavorites = [...this.favoriteNews];
    if (updatedFavorites.includes(id)) {
      const index = updatedFavorites.indexOf(id);
      updatedFavorites.splice(index, 1);
    } else {
      updatedFavorites.push(id);
    }
    this.favoriteNews = updatedFavorites;
    this.saveToStorage(this.STORAGE_KEYS.FAVORITE_NEWS, updatedFavorites);
  }

  // 阅读新闻处理(核心逻辑复用)
  private handleReadMore(news: NewsItem) {
    // 更新阅读历史
    const updatedHistory = [news.id, ...this.readHistory.filter(id => id !== news.id)].slice(0, 20);
    this.readHistory = updatedHistory;
    this.saveToStorage(this.STORAGE_KEYS.READ_HISTORY, updatedHistory);

    // 鸿蒙弹窗替代 Alert.alert
    AlertDialog.show({
      title: '阅读新闻',
      message: `正在阅读: ${news.title}\n\n${news.content}`,
      confirm: { value: '关闭' }
    });
  }

  // 搜索处理
  private handleSearch() {
    AlertDialog.show({
      title: '搜索',
      message: '打开新闻搜索功能',
      confirm: { value: '确定' }
    });
  }

  // 获取收藏的新闻
  private getFavoriteNews(): NewsItem[] {
    return this.newsItems.filter(item => this.favoriteNews.includes(item.id));
  }

  // 获取阅读历史
  private getReadHistory(): NewsItem[] {
    return this.newsItems.filter(item => this.readHistory.includes(item.id));
  }

  build() {
    Column({ space: 0 }) {
      // 头部组件
      this.Header();
      
      // 内容区域
      Scroll() {
        Column({ space: 16 }) {
          // 搜索栏
          this.SearchBar();

          // 标签页切换
          this.TabContainer();

          // 内容区域 - 条件渲染
          if (this.activeTab === 'news') {
            this.NewsContent();
          } else if (this.activeTab === 'favorites') {
            this.FavoriteContent();
          } else if (this.activeTab === 'history') {
            this.HistoryContent();
          }

          // 推荐新闻
          this.RecommendationSection();

          // 使用说明
          this.InfoCard();
        }
        .padding(16)
        .width('100%');
      }
      .flex(1)
      .width('100%');

      // 底部导航
      this.BottomNav();
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#f8fafc')
    .safeArea(true);
  }

  // 头部组件
  @Builder
  Header() {
    Row({ space: 0 }) {
      Text('今日资讯')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
        .fontColor('#1e293b');
      
      Row({ space: 12 }) {
        // 搜索按钮
        Button() {
          Text('🔍')
            .fontSize(18)
            .fontColor('#64748b');
        }
        .width(36)
        .height(36)
        .borderRadius(18)
        .backgroundColor('#f1f5f9')
        .stateEffect(false)
        .onClick(() => this.handleSearch());
        
        // 通知按钮
        Button() {
          Text('🔔')
            .fontSize(18)
            .fontColor('#64748b');
        }
        .width(36)
        .height(36)
        .borderRadius(18)
        .backgroundColor('#f1f5f9')
        .stateEffect(false);
      }
      .marginLeft('auto');
    }
    .padding(20)
    .backgroundColor('#ffffff')
    .borderBottom({ width: 1, color: '#e2e8f0' })
    .width('100%');
  }

  // 搜索栏组件
  @Builder
  SearchBar() {
    Row({ space: 12 }) {
      Text('🔍')
        .fontSize(18)
        .fontColor('#64748b');
      
      Text('搜索您关心的新闻')
        .fontSize(14)
        .fontColor('#94a3b8')
        .flex(1);
    }
    .backgroundColor('#ffffff')
    .borderRadius(20)
    .padding({ top: 12, bottom: 12, left: 16, right: 16 })
    .cardShadow()
    .width('100%');
  }

  // 标签页容器
  @Builder
  TabContainer() {
    Row({ space: 4 }) {
      // 最新新闻标签
      Button() {
        Text('最新新闻')
          .fontSize(14)
          .fontColor(this.activeTab === 'news' ? '#ffffff' : '#64748b')
          .fontWeight(this.activeTab === 'news' ? FontWeight.Medium : FontWeight.Normal);
      }
      .flex(1)
      .paddingVertical(10)
      .borderRadius(16)
      .backgroundColor(this.activeTab === 'news' ? '#3b82f6' : '#ffffff')
      .stateEffect(false)
      .onClick(() => this.activeTab = 'news');
      
      // 收藏标签
      Button() {
        Text('收藏')
          .fontSize(14)
          .fontColor(this.activeTab === 'favorites' ? '#ffffff' : '#64748b')
          .fontWeight(this.activeTab === 'favorites' ? FontWeight.Medium : FontWeight.Normal);
      }
      .flex(1)
      .paddingVertical(10)
      .borderRadius(16)
      .backgroundColor(this.activeTab === 'favorites' ? '#3b82f6' : '#ffffff')
      .stateEffect(false)
      .onClick(() => this.activeTab = 'favorites');
      
      // 阅读历史标签
      Button() {
        Text('阅读历史')
          .fontSize(14)
          .fontColor(this.activeTab === 'history' ? '#ffffff' : '#64748b')
          .fontWeight(this.activeTab === 'history' ? FontWeight.Medium : FontWeight.Normal);
      }
      .flex(1)
      .paddingVertical(10)
      .borderRadius(16)
      .backgroundColor(this.activeTab === 'history' ? '#3b82f6' : '#ffffff')
      .stateEffect(false)
      .onClick(() => this.activeTab = 'history');
    }
    .backgroundColor('#ffffff')
    .borderRadius(20)
    .padding(4)
    .width('100%');
  }

  // 新闻内容区域
  @Builder
  NewsContent() {
    Column({ space: 16 }) {
      // 热点新闻标题
      Text('热点新闻')
        .fontSize(18)
        .fontWeight(FontWeight.Bold)
        .fontColor('#1e293b')
        .width('100%');
      
      // 热点新闻横向滚动
      Scroll({ direction: ScrollDirection.Horizontal }) {
        Row({ space: 12 }) {
          const trendingNews = this.newsItems.filter(item => item.isBreaking).slice(0, 2);
          ForEach(trendingNews, (item: NewsItem) => {
            this.TrendingNewsCard(item);
          });
        }
      }
      .scrollBar(BarState.Off); // 隐藏滚动条
      
      // 新闻分类标题
      Text('新闻分类')
        .fontSize(18)
        .fontWeight(FontWeight.Bold)
        .fontColor('#1e293b')
        .width('100%');
      
      // 新闻分类横向滚动
      Scroll({ direction: ScrollDirection.Horizontal }) {
        Row({ space: 12 }) {
          const categories: Category[] = [
            { id: '1', name: '推荐', icon: '🔥' },
            { id: '2', name: '国内', icon: '🇨🇳' },
            // 其他分类...
          ];
          
          ForEach(categories, (category: Category) => {
            this.CategoryCard(category);
          });
        }
      }
      .scrollBar(BarState.Off);
      
      // 最新新闻标题
      Row({ space: 0 }) {
        Text('最新新闻')
          .fontSize(18)
          .fontWeight(FontWeight.Bold)
          .fontColor('#1e293b');
        
        Text(`(${this.newsItems.length} 条新闻)`)
          .fontSize(14)
          .fontColor('#64748b')
          .marginLeft('auto');
      }
      .width('100%');
      
      // 新闻列表(鸿蒙 List 替代 FlatList)
      List() {
        LazyForEach(new MyDataSource(this.newsItems), (item: NewsItem) => {
          ListItem() {
            this.NewsCard(item);
          }
          .marginBottom(16);
        });
      }
      .width('100%')
      .scrollBar(BarState.Off);
    }
    .width('100%');
  }

  // 收藏内容区域
  @Builder
  FavoriteContent() {
    Column({ space: 12 }) {
      Text('收藏的新闻')
        .fontSize(18)
        .fontWeight(FontWeight.Bold)
        .fontColor('#1e293b')
        .width('100%');
      
      if (this.getFavoriteNews().length > 0) {
        List() {
          LazyForEach(new MyDataSource(this.getFavoriteNews()), (item: NewsItem) => {
            ListItem() {
              this.NewsCard(item);
            }
            .marginBottom(16);
          });
        }
        .width('100%')
        .scrollBar(BarState.Off);
      } else {
        // 空状态
        Column({ space: 8 }) {
          Text('暂无收藏的新闻')
            .fontSize(16)
            .fontColor('#64748b');
          
          Text('点击新闻右下角的星星按钮可收藏')
            .fontSize(14)
            .fontColor('#94a3b8');
        }
        .padding({ top: 40, bottom: 40 })
        .width('100%')
        .alignItems(Alignment.Center);
      }
    }
    .width('100%');
  }

  // 阅读历史内容区域
  @Builder
  HistoryContent() {
    Column({ space: 12 }) {
      Text('阅读历史')
        .fontSize(18)
        .fontWeight(FontWeight.Bold)
        .fontColor('#1e293b')
        .width('100%');
      
      if (this.getReadHistory().length > 0) {
        List() {
          LazyForEach(new MyDataSource(this.getReadHistory()), (item: NewsItem) => {
            ListItem() {
              this.NewsCard(item);
            }
            .marginBottom(16);
          });
        }
        .width('100%')
        .scrollBar(BarState.Off);
      } else {
        // 空状态
        Column({ space: 8 }) {
          Text('暂无阅读历史')
            .fontSize(16)
            .fontColor('#64748b');
          
          Text('阅读过的新闻会在这里显示')
            .fontSize(14)
            .fontColor('#94a3b8');
        }
        .padding({ top: 40, bottom: 40 })
        .width('100%')
        .alignItems(Alignment.Center);
      }
    }
    .width('100%');
  }

  // 推荐新闻区域
  @Builder
  RecommendationSection() {
    Column({ space: 12 }) {
      Text('为您推荐')
        .fontSize(18)
        .fontWeight(FontWeight.Bold)
        .fontColor('#1e293b')
        .width('100%');
      
      Row({ space: 12 }) {
        // 推荐新闻图片占位
        Stack() {
          Text('📰')
            .fontSize(24)
            .fontColor('#64748b');
        }
        .width(60)
        .height(60)
        .borderRadius(8)
        .backgroundColor('#f1f5f9')
        .alignItems(Alignment.Center)
        .justifyContent(FlexAlign.Center);
        
        // 推荐新闻信息
        Column({ space: 4 }) {
          Text('人工智能在医疗领域的应用前景广阔')
            .fontSize(14)
            .fontWeight(FontWeight.Medium)
            .fontColor('#1e293b');
          
          Text('AI技术正在改变传统医疗模式,提高诊断效率和准确性')
            .fontSize(12)
            .fontColor('#64748b');
          
          Row({ space: 0 }) {
            Text('科技')
              .fontSize(11)
              .fontColor('#3b82f6')
              .backgroundColor('#dbeafe')
              .padding({ left: 6, right: 6, top: 2, bottom: 2 })
              .borderRadius(6);
            
            Text('2小时前')
              .fontSize(11)
              .fontColor('#94a3b8')
              .marginLeft('auto');
          }
        }
        .flex(1);
      }
    }
    .backgroundColor('#ffffff')
    .borderRadius(12)
    .padding(16)
    .cardShadow()
    .width('100%');
  }

  // 使用说明卡片
  @Builder
  InfoCard() {
    Column({ space: 8 }) {
      Text('使用说明')
        .fontSize(16)
        .fontWeight(FontWeight.Bold)
        .fontColor('#1e293b');
      
      Text('• 点赞新闻:点击👍按钮')
        .fontSize(14)
        .fontColor('#64748b')
        .lineHeight(22);
      
      Text('• 收藏新闻:点击⭐按钮')
        .fontSize(14)
        .fontColor('#64748b')
        .lineHeight(22);
      
      Text('• 阅读历史:自动保存最近阅读的新闻')
        .fontSize(14)
        .fontColor('#64748b')
        .lineHeight(22);
      
      Text('• 本地存储:所有数据都保存在您的设备上')
        .fontSize(14)
        .fontColor('#64748b')
        .lineHeight(22);
    }
    .backgroundColor('#ffffff')
    .borderRadius(12)
    .padding(16)
    .cardShadow()
    .width('100%');
  }

  // 底部导航
  @Builder
  BottomNav() {
    Row({ space: 0 }) {
      // 首页
      Button() {
        Column({ space: 4 }) {
          Text('🏠')
            .fontSize(20)
            .fontColor('#94a3b8');
          
          Text('首页')
            .fontSize(12)
            .fontColor('#94a3b8');
        }
      }
      .flex(1)
      .backgroundColor(Color.Transparent)
      .stateEffect(false);
      
      // 热点
      Button() {
        Column({ space: 4 }) {
          Text('🔥')
            .fontSize(20)
            .fontColor('#94a3b8');
          
          Text('热点')
            .fontSize(12)
            .fontColor('#94a3b8');
        }
      }
      .flex(1)
      .backgroundColor(Color.Transparent)
      .stateEffect(false);
      
      // 收藏
      Button() {
        Column({ space: 4 }) {
          Text('⭐')
            .fontSize(20)
            .fontColor('#94a3b8');
          
          Text('收藏')
            .fontSize(12)
            .fontColor('#94a3b8');
        }
      }
      .flex(1)
      .backgroundColor(Color.Transparent)
      .stateEffect(false);
      
      // 我的
      Button() {
        Column({ space: 4 }) {
          Text('👤')
            .fontSize(20)
            .fontColor('#94a3b8');
          
          Text('我的')
            .fontSize(12)
            .fontColor('#94a3b8');
        }
      }
      .flex(1)
      .backgroundColor(Color.Transparent)
      .stateEffect(false);
    }
    .backgroundColor('#ffffff')
    .borderTop({ width: 1, color: '#e2e8f0' })
    .paddingVertical(12)
    .width('100%');
  }

  // 新闻卡片组件
  @Builder
  NewsCard(news: NewsItem) {
    Column({ space: 8 }) {
      // 新闻头部(分类 + 热点)
      Row({ space: 0 }) {
        // 分类标签
        Text(news.category)
          .fontSize(12)
          .fontWeight(FontWeight.Medium)
          .fontColor('#3b82f6')
          .backgroundColor('#dbeafe')
          .padding({ left: 8, right: 8, top: 4, bottom: 4 })
          .borderRadius(12);
        
        // 热点标识
        if (news.isBreaking) {
          Text('🔥 热点')
            .fontSize(12)
            .fontWeight(FontWeight.Medium)
            .fontColor('#dc2626')
            .backgroundColor('#fed7d7')
            .padding({ left: 8, right: 8, top: 4, bottom: 4 })
            .borderRadius(12)
            .marginLeft('auto');
        }
      }
      .width('100%');
      
      // 新闻标题和摘要
      Column({ space: 8 }) {
        Text(news.title)
          .fontSize(16)
          .fontWeight(FontWeight.Bold)
          .fontColor('#1e293b');
        
        Text(news.summary)
          .fontSize(14)
          .fontColor('#64748b')
          .lineHeight(20)
          .maxLines(2);
      }
      .width('100%')
      .onClick(() => this.handleReadMore(news));
      
      // 元信息
      Row({ space: 0 }) {
        Column({ space: 0 }) {
          Text(news.author)
            .fontSize(12)
            .fontColor('#64748b');
        }
        .marginRight(12);
        
        Text(news.publishTime)
          .fontSize(12)
          .fontColor('#94a3b8')
          .marginRight('auto');
        
        Text(`${news.readTime} 分钟阅读`)
          .fontSize(12)
          .fontColor('#94a3b8');
      }
      .width('100%')
      .paddingBottom(12)
      .borderBottom({ width: 1, color: '#e2e8f0' });
      
      // 操作按钮
      Row({ space: 0 }) {
        // 点赞按钮
        Button() {
          Row({ space: 4 }) {
            Text('👍')
              .fontSize(16)
              .fontColor(news.isLiked ? '#ef4444' : '#64748b');
            
            Text(news.likes.toString())
              .fontSize(12)
              .fontColor('#64748b');
          }
        }
        .backgroundColor(Color.Transparent)
        .stateEffect(false)
        .onClick(() => this.handleLike(news.id));
        
        // 评论按钮
        Button() {
          Row({ space: 4 }) {
            Text('💬')
              .fontSize(16)
              .fontColor('#64748b');
            
            Text(news.comments.toString())
              .fontSize(12)
              .fontColor('#64748b');
          }
        }
        .backgroundColor(Color.Transparent)
        .stateEffect(false)
        .marginLeft('auto');
        
        // 收藏按钮
        Button() {
          Row({ space: 4 }) {
            Text(news.isBookmarked ? '⭐' : '☆')
              .fontSize(16)
              .fontColor(news.isBookmarked ? '#3b82f6' : '#64748b');
            
            Text(news.shares.toString())
              .fontSize(12)
              .fontColor('#64748b');
          }
        }
        .backgroundColor(Color.Transparent)
        .stateEffect(false)
        .onClick(() => this.handleBookmark(news.id));
        
        // 分享按钮
        Button() {
          Text('🔄')
            .fontSize(16)
            .fontColor('#64748b');
        }
        .backgroundColor(Color.Transparent)
        .stateEffect(false);
      }
      .width('100%');
    }
    .backgroundColor('#ffffff')
    .borderRadius(12)
    .padding(16)
    .cardShadow()
    .width('100%');
  }

  // 热点新闻卡片
  @Builder
  TrendingNewsCard(news: NewsItem) {
    Row({ space: 12 }) {
      // 图片占位
      Stack() {
        Text('📰')
          .fontSize(30)
          .fontColor('#64748b');
      }
      .width(80)
      .height(80)
      .borderRadius(8)
      .backgroundColor('#f1f5f9')
      .alignItems(Alignment.Center)
      .justifyContent(FlexAlign.Center);
      
      // 新闻内容
      Column({ space: 4 }) {
        Text(news.title)
          .fontSize(14)
          .fontWeight(FontWeight.SemiBold)
          .fontColor('#1e293b')
          .maxLines(2);
        
        Text(news.summary)
          .fontSize(12)
          .fontColor('#64748b')
          .maxLines(2)
          .flex(1);
        
        Row({ space: 0 }) {
          Text(news.author)
            .fontSize(11)
            .fontColor('#94a3b8');
          
          Text(news.publishTime)
            .fontSize(11)
            .fontColor('#94a3b8')
            .marginLeft('auto');
        }
      }
      .flex(1);
    }
    .backgroundColor('#ffffff')
    .borderRadius(12)
    .padding(12)
    .cardShadow()
    .width('80%')
    .marginRight(12)
    .onClick(() => this.handleReadMore(news));
  }

  // 分类卡片
  @Builder
  CategoryCard(category: Category) {
    Button() {
      Column({ space: 8 }) {
        Stack() {
          Text(category.icon)
            .fontSize(16)
            .fontColor('#3b82f6');
        }
        .width(32)
        .height(32)
        .borderRadius(16)
        .backgroundColor('#dbeafe')
        .alignItems(Alignment.Center)
        .justifyContent(FlexAlign.Center);
        
        Text(category.name)
          .fontSize(12)
          .fontColor('#1e293b')
          .textAlign(TextAlign.Center);
      }
    }
    .width(70)
    .backgroundColor('#ffffff')
    .borderRadius(12)
    .padding(16)
    .cardShadow()
    .marginRight(12)
    .stateEffect(false)
    .onClick(() => {
      AlertDialog.show({
        title: '类别筛选',
        message: `显示 ${category.name} 类别的新闻`,
        confirm: { value: '确定' }
      });
    });
  }
}

// 自定义数据源(鸿蒙 LazyForEach 必需)
class MyDataSource implements IDataSource {
  private data: NewsItem[];
  private listener: DataChangeListener | null = null;

  constructor(data: NewsItem[]) {
    this.data = data;
  }

  totalCount(): number {
    return this.data.length;
  }

  getData(index: number): NewsItem {
    return this.data[index];
  }

  registerDataChangeListener(listener: DataChangeListener): void {
    this.listener = listener;
  }

  unregisterDataChangeListener(): void {
    this.listener = null;
  }
}

(1)本地存储

React Native AsyncStorage 替换为鸿蒙 Preferences

// React Native
await AsyncStorage.setItem(key, JSON.stringify(data));

// 鸿蒙
await this.prefManager.put(key, JSON.stringify(data));
await this.prefManager.flush();
(2)列表渲染

React Native FlatList 替换为鸿蒙 List + LazyForEach

// React Native
<FlatList
  data={newsItems}
  renderItem={({ item }) => <NewsCard news={item} />}
/>

// 鸿蒙
List() {
  LazyForEach(new MyDataSource(this.newsItems), (item: NewsItem) => {
    ListItem() {
      this.NewsCard(item);
    }
  });
}
(3)状态更新
// React Native(不可变更新)
const updatedNews = newsItems.map(item => 
  item.id === id ? { ...item, isLiked: !item.isLiked } : item
);
setNewsItems(updatedNews);

// 鸿蒙(直接更新)
this.newsItems = this.newsItems.map(item => 
  item.id === id ? { ...item, isLiked: !item.isLiked } : item
);
(4)生命周期
// React Native
useEffect(() => {
  loadStoredData();
}, []);

// 鸿蒙
async aboutToAppear() {
  await this.initPreferences();
  await this.loadStoredData();
}

该新闻资讯应用的跨端适配实践验证了 React Native 向鸿蒙迁移的可行性与高效性,对于内容类应用,可实现较高的代码复用率,同时保证一致的用户体验和业务逻辑。这种适配模式特别适合需要快速覆盖多端的内容类应用开发。


真实演示案例代码:







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

// Base64 图标库
const ICONS_BASE64 = {
  news: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
  like: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
  comment: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
  share: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
  bookmark: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
  search: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
  home: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
  trending: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
};

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

// 新闻类型
type NewsItem = {
  id: string;
  title: string;
  summary: string;
  content: string;
  author: string;
  publishTime: string;
  category: string;
  readTime: string;
  likes: number;
  comments: number;
  shares: number;
  isLiked: boolean;
  isBookmarked: boolean;
  isBreaking: boolean;
};

// 类别类型
type Category = {
  id: string;
  name: string;
  icon: string;
};

// 本地存储键
const STORAGE_KEYS = {
  NEWS_ITEMS: '@news_items',
  FAVORITE_NEWS: '@favorite_news',
  READ_HISTORY: '@read_history',
};

// 初始化新闻数据
const initialNewsItems: NewsItem[] = [
  {
    id: '1',
    title: '全球气候变化峰会达成历史性协议',
    summary: '各国领导人就减排目标达成一致,承诺在未来十年内大幅减少碳排放。这一协议被认为是对抗气候变化的重要里程碑。',
    content: '全球气候变化峰会在经过两周的紧张谈判后,各国代表终于达成了一项历史性的协议。根据协议内容,所有签署国承诺在未来十年内将其温室气体排放量减少50%,并投入大量资金用于绿色能源的研发和推广。专家认为,这项协议将对全球气候产生深远影响。',
    author: '张记者',
    publishTime: '2小时前',
    category: '国际',
    readTime: '5',
    likes: 245,
    comments: 42,
    shares: 18,
    isLiked: false,
    isBookmarked: true,
    isBreaking: true
  },
  {
    id: '2',
    title: '科技创新大会聚焦人工智能发展',
    summary: '多家科技巨头展示了最新的人工智能研究成果,引发了业界广泛关注。',
    content: '在本届科技创新大会上,多家知名科技公司发布了其最新的人工智能研究成果。这些新技术涵盖了机器学习、自然语言处理等多个领域,预计将对未来的生活和工作方式产生重大影响。',
    author: '李科技',
    publishTime: '4小时前',
    category: '科技',
    readTime: '3',
    likes: 189,
    comments: 35,
    shares: 22,
    isLiked: true,
    isBookmarked: false,
    isBreaking: false
  },
  {
    id: '3',
    title: '体育界传来好消息:奥运会筹备进展顺利',
    summary: '组委会宣布各项设施建设和准备工作均已按计划完成,预计将迎来史上最成功的奥运会。',
    content: '国际奥委会主席在新闻发布会上表示,本届奥运会的各项筹备工作进展非常顺利。所有比赛场馆已基本完工,志愿者招募工作也已完成。此外,防疫措施也在不断完善,确保运动员和观众的安全。',
    author: '王体育',
    publishTime: '6小时前',
    category: '体育',
    readTime: '4',
    likes: 156,
    comments: 28,
    shares: 15,
    isLiked: false,
    isBookmarked: false,
    isBreaking: true
  },
  {
    id: '4',
    title: '健康生活:新研究揭示饮食习惯对长寿的影响',
    summary: '科学家发现特定的饮食模式可以显著延长寿命,这一发现对公共卫生具有重要意义。',
    content: '最新的科学研究表明,采用地中海饮食模式的人群平均寿命比普通人群高出约5年。这种饮食模式富含橄榄油、鱼类、坚果和蔬菜,有助于降低心血管疾病的风险。',
    author: '赵医生',
    publishTime: '8小时前',
    category: '健康',
    readTime: '6',
    likes: 98,
    comments: 17,
    shares: 12,
    isLiked: false,
    isBookmarked: true,
    isBreaking: false
  },
  {
    id: '5',
    title: '经济复苏势头强劲,失业率降至新低',
    summary: '最新数据显示,经济复苏速度超出预期,就业市场表现亮眼。',
    content: '国家统计局公布的最新数据显示,上个月失业率降至3.2%,为近十年来的最低水平。同时,新增就业岗位达到25万个,显示出经济复苏的强劲势头。经济学家预测,这一趋势有望在接下来的几个月内持续。',
    author: '钱经济',
    publishTime: '10小时前',
    category: '财经',
    readTime: '4',
    likes: 134,
    comments: 25,
    shares: 19,
    isLiked: true,
    isBookmarked: false,
    isBreaking: true
  }
];

const NewsApp = () => {
  const [newsItems, setNewsItems] = useState<NewsItem[]>(initialNewsItems);
  const [favoriteNews, setFavoriteNews] = useState<string[]>([]);
  const [readHistory, setReadHistory] = useState<string[]>([]);
  const [activeTab, setActiveTab] = useState<'news' | 'favorites' | 'history'>('news');

  // 加载本地存储的数据
  useEffect(() => {
    loadStoredData();
  }, []);

  const loadStoredData = async () => {
    try {
      const storedNews = await AsyncStorage.getItem(STORAGE_KEYS.NEWS_ITEMS);
      if (storedNews) {
        setNewsItems(JSON.parse(storedNews));
      }

      const storedFavorites = await AsyncStorage.getItem(STORAGE_KEYS.FAVORITE_NEWS);
      if (storedFavorites) {
        setFavoriteNews(JSON.parse(storedFavorites));
      }

      const storedHistory = await AsyncStorage.getItem(STORAGE_KEYS.READ_HISTORY);
      if (storedHistory) {
        setReadHistory(JSON.parse(storedHistory));
      }
    } catch (error) {
      console.error('加载本地数据失败:', error);
    }
  };

  // 保存数据到本地存储
  const saveToStorage = async (key: string, data: any) => {
    try {
      await AsyncStorage.setItem(key, JSON.stringify(data));
    } catch (error) {
      console.error('保存数据失败:', error);
    }
  };

  const handleLike = (id: string) => {
    const updatedNews = newsItems.map(item => 
      item.id === id 
        ? { ...item, likes: item.isLiked ? item.likes - 1 : item.likes + 1, isLiked: !item.isLiked } 
        : item
    );
    setNewsItems(updatedNews);
    saveToStorage(STORAGE_KEYS.NEWS_ITEMS, updatedNews);
  };

  const handleBookmark = (id: string) => {
    const updatedNews = newsItems.map(item => 
      item.id === id 
        ? { ...item, isBookmarked: !item.isBookmarked } 
        : item
    );
    setNewsItems(updatedNews);
    saveToStorage(STORAGE_KEYS.NEWS_ITEMS, updatedNews);

    // 更新收藏列表
    const updatedFavorites = [...favoriteNews];
    if (updatedFavorites.includes(id)) {
      const index = updatedFavorites.indexOf(id);
      updatedFavorites.splice(index, 1);
    } else {
      updatedFavorites.push(id);
    }
    setFavoriteNews(updatedFavorites);
    saveToStorage(STORAGE_KEYS.FAVORITE_NEWS, updatedFavorites);
  };

  const handleReadMore = (news: NewsItem) => {
    // 更新阅读历史
    const updatedHistory = [news.id, ...readHistory.filter(id => id !== news.id)].slice(0, 20);
    setReadHistory(updatedHistory);
    saveToStorage(STORAGE_KEYS.READ_HISTORY, updatedHistory);

    Alert.alert('阅读新闻', `正在阅读: ${news.title}\n\n${news.content}`);
  };

  const handleSearch = () => {
    Alert.alert('搜索', '打开新闻搜索功能');
  };

  // 获取收藏的新闻
  const getFavoriteNews = () => {
    return newsItems.filter(item => favoriteNews.includes(item.id));
  };

  // 获取阅读历史
  const getReadHistory = () => {
    return newsItems.filter(item => readHistory.includes(item.id));
  };

  // 新闻卡片组件
  const NewsCard = ({ 
    news, 
    onLike,
    onBookmark,
    onReadMore
  }: { 
    news: NewsItem; 
    onLike: (id: string) => void;
    onBookmark: (id: string) => void;
    onReadMore: (news: NewsItem) => void;
  }) => {
    return (
      <View style={styles.newsCard}>
        <View style={styles.newsHeader}>
          <View style={styles.categoryBadge}>
            <Text style={styles.categoryText}>{news.category}</Text>
          </View>
          {news.isBreaking && (
            <View style={styles.breakingBadge}>
              <Text style={styles.breakingText}>🔥 热点</Text>
            </View>
          )}
        </View>
        
        <TouchableOpacity onPress={() => onReadMore(news)}>
          <Text style={styles.newsTitle}>{news.title}</Text>
          <Text style={styles.newsSummary} numberOfLines={2}>{news.summary}</Text>
        </TouchableOpacity>
        
        <View style={styles.newsMeta}>
          <View style={styles.authorInfo}>
            <Text style={styles.authorName}>{news.author}</Text>
            <Text style={styles.publishTime}>{news.publishTime}</Text>
          </View>
          <Text style={styles.readTime}>{news.readTime} 分钟阅读</Text>
        </View>
        
        <View style={styles.newsActions}>
          <TouchableOpacity style={styles.actionButton} onPress={() => onLike(news.id)}>
            <Text style={[styles.actionIcon, news.isLiked && styles.likedIcon]}>👍</Text>
            <Text style={styles.actionText}>{news.likes}</Text>
          </TouchableOpacity>
          <TouchableOpacity style={styles.actionButton}>
            <Text style={styles.actionIcon}>💬</Text>
            <Text style={styles.actionText}>{news.comments}</Text>
          </TouchableOpacity>
          <TouchableOpacity style={styles.actionButton} onPress={() => onBookmark(news.id)}>
            <Text style={[styles.actionIcon, news.isBookmarked && styles.bookmarkedIcon]}>
              {news.isBookmarked ? '⭐' : '☆'}
            </Text>
            <Text style={styles.actionText}>{news.shares}</Text>
          </TouchableOpacity>
          <TouchableOpacity style={styles.actionButton}>
            <Text style={styles.actionIcon}>🔄</Text>
          </TouchableOpacity>
        </View>
      </View>
    );
  };

  // 类别卡片组件
  const CategoryCard = ({ 
    category, 
    onPress 
  }: { 
    category: Category; 
    onPress: () => void 
  }) => {
    return (
      <TouchableOpacity style={styles.categoryCard} onPress={onPress}>
        <View style={styles.categoryIcon}>
          <Text style={styles.categoryIconText}>{category.icon}</Text>
        </View>
        <Text style={styles.categoryName}>{category.name}</Text>
      </TouchableOpacity>
    );
  };

  // 热点新闻卡片组件
  const TrendingNewsCard = ({ 
    news, 
    onPress 
  }: { 
    news: NewsItem; 
    onPress: (news: NewsItem) => void 
  }) => {
    return (
      <TouchableOpacity style={styles.trendingCard} onPress={() => onPress(news)}>
        <View style={styles.trendingImage}>
          <Text style={styles.trendingImageText}>📰</Text>
        </View>
        <View style={styles.trendingContent}>
          <Text style={styles.trendingTitle} numberOfLines={2}>{news.title}</Text>
          <Text style={styles.trendingSummary} numberOfLines={2}>{news.summary}</Text>
          <View style={styles.trendingMeta}>
            <Text style={styles.trendingAuthor}>{news.author}</Text>
            <Text style={styles.trendingTime}>{news.publishTime}</Text>
          </View>
        </View>
      </TouchableOpacity>
    );
  };

  const categories: Category[] = [
    { id: '1', name: '推荐', icon: '🔥' },
    { id: '2', name: '国内', icon: '🇨🇳' },
    { id: '3', name: '国际', icon: '🌍' },
    { id: '4', name: '科技', icon: '💻' },
    { id: '5', name: '体育', icon: '⚽' },
    { id: '6', name: '财经', icon: '💰' },
    { id: '7', name: '娱乐', icon: '🎬' },
    { id: '8', name: '健康', icon: '🏥' },
  ];

  const trendingNews = newsItems.filter(item => item.isBreaking).slice(0, 2);

  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}>🔍</Text>
          </TouchableOpacity>
          <TouchableOpacity style={styles.bellButton}>
            <Text style={styles.bellIcon}>🔔</Text>
          </TouchableOpacity>
        </View>
      </View>

      <ScrollView style={styles.content}>
        {/* 搜索栏 */}
        <View style={styles.searchContainer}>
          <Text style={styles.searchIcon}>🔍</Text>
          <Text style={styles.searchPlaceholder}>搜索您关心的新闻</Text>
        </View>

        {/* 标签页切换 */}
        <View style={styles.tabContainer}>
          <TouchableOpacity 
            style={[styles.tab, activeTab === 'news' && styles.activeTab]}
            onPress={() => setActiveTab('news')}
          >
            <Text style={[styles.tabText, activeTab === 'news' && styles.activeTabText]}>最新新闻</Text>
          </TouchableOpacity>
          <TouchableOpacity 
            style={[styles.tab, activeTab === 'favorites' && styles.activeTab]}
            onPress={() => setActiveTab('favorites')}
          >
            <Text style={[styles.tabText, activeTab === 'favorites' && styles.activeTabText]}>收藏</Text>
          </TouchableOpacity>
          <TouchableOpacity 
            style={[styles.tab, activeTab === 'history' && styles.activeTab]}
            onPress={() => setActiveTab('history')}
          >
            <Text style={[styles.tabText, activeTab === 'history' && styles.activeTabText]}>阅读历史</Text>
          </TouchableOpacity>
        </View>

        {/* 内容区域 */}
        {activeTab === 'news' && (
          <>
            {/* 热点新闻轮播 */}
            <Text style={styles.sectionTitle}>热点新闻</Text>
            <ScrollView horizontal showsHorizontalScrollIndicator={false} style={styles.trendingContainer}>
              <View style={styles.trendingList}>
                {trendingNews.map(item => (
                  <TrendingNewsCard
                    key={item.id}
                    news={item}
                    onPress={handleReadMore}
                  />
                ))}
              </View>
            </ScrollView>

            {/* 新闻分类 */}
            <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={() => Alert.alert('类别筛选', `显示 ${category.name} 类别的新闻`)}
                  />
                ))}
              </View>
            </ScrollView>

            {/* 新闻列表标题 */}
            <View style={styles.sectionHeader}>
              <Text style={styles.sectionTitle}>最新新闻</Text>
              <Text style={styles.newsCount}>({newsItems.length} 条新闻)</Text>
            </View>

            {/* 新闻列表 */}
            <FlatList
              data={newsItems}
              keyExtractor={item => item.id}
              renderItem={({ item }) => (
                <NewsCard
                  news={item}
                  onLike={handleLike}
                  onBookmark={handleBookmark}
                  onReadMore={handleReadMore}
                />
              )}
              showsVerticalScrollIndicator={false}
            />
          </>
        )}

        {activeTab === 'favorites' && (
          <View>
            <Text style={styles.sectionTitle}>收藏的新闻</Text>
            {getFavoriteNews().length > 0 ? (
              <FlatList
                data={getFavoriteNews()}
                keyExtractor={item => item.id}
                renderItem={({ item }) => (
                  <NewsCard
                    news={item}
                    onLike={handleLike}
                    onBookmark={handleBookmark}
                    onReadMore={handleReadMore}
                  />
                )}
                showsVerticalScrollIndicator={false}
              />
            ) : (
              <View style={styles.emptyState}>
                <Text style={styles.emptyStateText}>暂无收藏的新闻</Text>
                <Text style={styles.emptyStateSubtext}>点击新闻右下角的星星按钮可收藏</Text>
              </View>
            )}
          </View>
        )}

        {activeTab === 'history' && (
          <View>
            <Text style={styles.sectionTitle}>阅读历史</Text>
            {getReadHistory().length > 0 ? (
              <FlatList
                data={getReadHistory()}
                keyExtractor={item => item.id}
                renderItem={({ item }) => (
                  <NewsCard
                    news={item}
                    onLike={handleLike}
                    onBookmark={handleBookmark}
                    onReadMore={handleReadMore}
                  />
                )}
                showsVerticalScrollIndicator={false}
              />
            ) : (
              <View style={styles.emptyState}>
                <Text style={styles.emptyStateText}>暂无阅读历史</Text>
                <Text style={styles.emptyStateSubtext}>阅读过的新闻会在这里显示</Text>
              </View>
            )}
          </View>
        )}

        {/* 推荐新闻 */}
        <View style={styles.recommendationSection}>
          <Text style={styles.sectionTitle}>为您推荐</Text>
          <View style={styles.recommendationItem}>
            <View style={styles.recommendationImage}>
              <Text style={styles.recommendationImageText}>📰</Text>
            </View>
            <View style={styles.recommendationInfo}>
              <Text style={styles.recommendationTitle}>人工智能在医疗领域的应用前景广阔</Text>
              <Text style={styles.recommendationSummary}>AI技术正在改变传统医疗模式,提高诊断效率和准确性</Text>
              <View style={styles.recommendationMeta}>
                <Text style={styles.recommendationCategory}>科技</Text>
                <Text style={styles.recommendationTime}>2小时前</Text>
              </View>
            </View>
          </View>
        </View>

        {/* 使用说明 */}
        <View style={styles.infoCard}>
          <Text style={styles.infoTitle}>使用说明</Text>
          <Text style={styles.infoText}>• 点赞新闻:点击👍按钮</Text>
          <Text style={styles.infoText}>• 收藏新闻:点击⭐按钮</Text>
          <Text style={styles.infoText}>• 阅读历史:自动保存最近阅读的新闻</Text>
          <Text style={styles.infoText}>• 本地存储:所有数据都保存在您的设备上</Text>
        </View>
      </ScrollView>

      {/* 底部导航 */}
      <View style={styles.bottomNav}>
        <TouchableOpacity style={styles.navItem}>
          <Text style={styles.navIcon}>🏠</Text>
          <Text style={styles.navText}>首页</Text>
        </TouchableOpacity>
        <TouchableOpacity style={styles.navItem}>
          <Text style={styles.navIcon}>🔥</Text>
          <Text style={styles.navText}>热点</Text>
        </TouchableOpacity>
        <TouchableOpacity style={styles.navItem}>
          <Text style={styles.navIcon}></Text>
          <Text style={styles.navText}>收藏</Text>
        </TouchableOpacity>
        <TouchableOpacity style={styles.navItem}>
          <Text style={styles.navIcon}>👤</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',
  },
  bellButton: {
    width: 36,
    height: 36,
    borderRadius: 18,
    backgroundColor: '#f1f5f9',
    alignItems: 'center',
    justifyContent: 'center',
  },
  bellIcon: {
    fontSize: 18,
    color: '#64748b',
  },
  content: {
    flex: 1,
    padding: 16,
  },
  searchContainer: {
    flexDirection: 'row',
    alignItems: 'center',
    backgroundColor: '#ffffff',
    borderRadius: 20,
    paddingVertical: 12,
    paddingHorizontal: 16,
    marginBottom: 16,
    elevation: 1,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 1 },
    shadowOpacity: 0.1,
    shadowRadius: 2,
  },
  searchPlaceholder: {
    fontSize: 14,
    color: '#94a3b8',
    marginLeft: 12,
    flex: 1,
  },
  sectionTitle: {
    fontSize: 18,
    fontWeight: 'bold',
    color: '#1e293b',
    marginVertical: 12,
  },
  sectionHeader: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
  },
  newsCount: {
    fontSize: 14,
    color: '#64748b',
  },
  trendingContainer: {
    marginBottom: 16,
  },
  trendingList: {
    flexDirection: 'row',
  },
  trendingCard: {
    backgroundColor: '#ffffff',
    borderRadius: 12,
    flexDirection: 'row',
    padding: 12,
    marginRight: 12,
    width: width * 0.8,
    elevation: 1,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 1 },
    shadowOpacity: 0.1,
    shadowRadius: 2,
  },
  trendingImage: {
    width: 80,
    height: 80,
    borderRadius: 8,
    backgroundColor: '#f1f5f9',
    alignItems: 'center',
    justifyContent: 'center',
    marginRight: 12,
  },
  trendingImageText: {
    fontSize: 30,
  },
  trendingContent: {
    flex: 1,
  },
  trendingTitle: {
    fontSize: 14,
    fontWeight: '600',
    color: '#1e293b',
    marginBottom: 4,
  },
  trendingSummary: {
    fontSize: 12,
    color: '#64748b',
    marginBottom: 8,
    flex: 1,
  },
  trendingMeta: {
    flexDirection: 'row',
    justifyContent: 'space-between',
  },
  trendingAuthor: {
    fontSize: 11,
    color: '#94a3b8',
  },
  trendingTime: {
    fontSize: 11,
    color: '#94a3b8',
  },
  categoriesContainer: {
    marginBottom: 16,
  },
  categoriesList: {
    flexDirection: 'row',
  },
  categoryCard: {
    backgroundColor: '#ffffff',
    borderRadius: 12,
    padding: 16,
    marginRight: 12,
    alignItems: 'center',
    width: 70,
    elevation: 1,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 1 },
    shadowOpacity: 0.1,
    shadowRadius: 2,
  },
  categoryIcon: {
    width: 32,
    height: 32,
    borderRadius: 16,
    backgroundColor: '#dbeafe',
    alignItems: 'center',
    justifyContent: 'center',
    marginBottom: 8,
  },
  categoryIconText: {
    fontSize: 16,
    color: '#3b82f6',
  },
  categoryName: {
    fontSize: 12,
    color: '#1e293b',
    textAlign: 'center',
  },
  newsCard: {
    backgroundColor: '#ffffff',
    borderRadius: 12,
    padding: 16,
    marginBottom: 16,
    elevation: 1,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 1 },
    shadowOpacity: 0.1,
    shadowRadius: 2,
  },
  newsHeader: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    marginBottom: 8,
  },
  categoryBadge: {
    backgroundColor: '#dbeafe',
    paddingHorizontal: 8,
    paddingVertical: 4,
    borderRadius: 12,
  },
  categoryText: {
    fontSize: 12,
    color: '#3b82f6',
    fontWeight: '500',
  },
  breakingBadge: {
    backgroundColor: '#fed7d7',
    paddingHorizontal: 8,
    paddingVertical: 4,
    borderRadius: 12,
  },
  breakingText: {
    fontSize: 12,
    color: '#dc2626',
    fontWeight: '500',
  },
  newsTitle: {
    fontSize: 16,
    fontWeight: 'bold',
    color: '#1e293b',
    marginBottom: 8,
  },
  newsSummary: {
    fontSize: 14,
    color: '#64748b',
    lineHeight: 20,
    marginBottom: 12,
  },
  newsMeta: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    marginBottom: 12,
    paddingBottom: 12,
    borderBottomWidth: 1,
    borderBottomColor: '#e2e8f0',
  },
  authorInfo: {
    flexDirection: 'row',
    alignItems: 'center',
  },
  authorName: {
    fontSize: 12,
    color: '#64748b',
    marginRight: 12,
  },
  publishTime: {
    fontSize: 12,
    color: '#94a3b8',
  },
  readTime: {
    fontSize: 12,
    color: '#94a3b8',
  },
  newsActions: {
    flexDirection: 'row',
    justifyContent: 'space-between',
  },
  actionButton: {
    flexDirection: 'row',
    alignItems: 'center',
  },
  actionIcon: {
    fontSize: 16,
    color: '#64748b',
    marginRight: 4,
  },
  likedIcon: {
    color: '#ef4444',
  },
  bookmarkedIcon: {
    color: '#3b82f6',
  },
  actionText: {
    fontSize: 12,
    color: '#64748b',
  },
  recommendationSection: {
    backgroundColor: '#ffffff',
    borderRadius: 12,
    padding: 16,
    marginTop: 16,
    elevation: 1,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 1 },
    shadowOpacity: 0.1,
    shadowRadius: 2,
  },
  recommendationItem: {
    flexDirection: 'row',
    alignItems: 'center',
  },
  recommendationImage: {
    width: 60,
    height: 60,
    borderRadius: 8,
    backgroundColor: '#f1f5f9',
    alignItems: 'center',
    justifyContent: 'center',
    marginRight: 12,
  },
  recommendationImageText: {
    fontSize: 24,
  },
  recommendationInfo: {
    flex: 1,
  },
  recommendationTitle: {
    fontSize: 14,
    fontWeight: '500',
    color: '#1e293b',
    marginBottom: 4,
  },
  recommendationSummary: {
    fontSize: 12,
    color: '#64748b',
    marginBottom: 6,
  },
  recommendationMeta: {
    flexDirection: 'row',
    justifyContent: 'space-between',
  },
  recommendationCategory: {
    fontSize: 11,
    color: '#3b82f6',
    backgroundColor: '#dbeafe',
    paddingHorizontal: 6,
    paddingVertical: 2,
    borderRadius: 6,
  },
  recommendationTime: {
    fontSize: 11,
    color: '#94a3b8',
  },
  infoCard: {
    backgroundColor: '#ffffff',
    borderRadius: 12,
    padding: 16,
    marginTop: 16,
    elevation: 1,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 1 },
    shadowOpacity: 0.1,
    shadowRadius: 2,
  },
  infoTitle: {
    fontSize: 16,
    fontWeight: 'bold',
    color: '#1e293b',
    marginBottom: 12,
  },
  infoText: {
    fontSize: 14,
    color: '#64748b',
    lineHeight: 22,
    marginBottom: 8,
  },
  emptyState: {
    alignItems: 'center',
    justifyContent: 'center',
    paddingVertical: 40,
  },
  emptyStateText: {
    fontSize: 16,
    color: '#64748b',
    marginBottom: 8,
  },
  emptyStateSubtext: {
    fontSize: 14,
    color: '#94a3b8',
  },
  tabContainer: {
    flexDirection: 'row',
    backgroundColor: '#ffffff',
    borderRadius: 20,
    padding: 4,
    marginBottom: 16,
  },
  tab: {
    flex: 1,
    paddingVertical: 10,
    alignItems: 'center',
    borderRadius: 16,
  },
  activeTab: {
    backgroundColor: '#3b82f6',
  },
  tabText: {
    fontSize: 14,
    color: '#64748b',
  },
  activeTabText: {
    color: '#ffffff',
    fontWeight: '500',
  },
  bottomNav: {
    flexDirection: 'row',
    justifyContent: 'space-around',
    backgroundColor: '#ffffff',
    borderTopWidth: 1,
    borderTopColor: '#e2e8f0',
    paddingVertical: 12,
  },
  navItem: {
    alignItems: 'center',
    flex: 1,
  },
  navIcon: {
    fontSize: 20,
    color: '#94a3b8',
    marginBottom: 4,
  },
  activeNavIcon: {
    color: '#3b82f6',
  },
  navText: {
    fontSize: 12,
    color: '#94a3b8',
  },
  activeNavText: {
    color: '#3b82f6',
    fontWeight: '500',
  },
});

export default NewsApp;

请添加图片描述


打包

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

在这里插入图片描述

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

在这里插入图片描述

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

请添加图片描述
本文分析了基于React Native的新闻应用实现方案,重点探讨了架构设计、状态管理和数据持久化策略。应用采用组件化架构,将主应用组件与新闻列表、功能按钮等子组件分离,实现关注点分离。状态管理使用useState钩子和TypeScript类型定义,确保数据结构的类型安全。通过AsyncStorage实现数据持久化,在组件挂载时加载本地数据,并同步更新内存状态和本地存储。文章还介绍了新闻管理功能、交互设计优化以及FlatList渲染优化等性能提升策略,为开发功能完备的跨平台新闻应用提供了参考方案。

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

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐