本文介绍了React Native骨架屏在鸿蒙生态中的实现方案。通过原子组件与组合组件的方式,构建了卡片、列表等通用占位结构,确保跨端布局一致性。动画采用原生驱动的透明度渐变,避免性能损耗。文章详细探讨了样式跨端适配、列表性能优化、状态管理及安全区域处理等关键技术点,特别强调在鸿蒙设备上需注意动画生命周期管理、布局稳定性和内存优化。通过类型化组件设计和智能动画循环机制,实现了流畅的加载体验,为React Native在鸿蒙平台的性能优化提供了实践参考。


这段“骨架屏”示例用最小依赖的 React Native 原语搭建出数据加载占位的完整体验:卡片/列表条目/统计占位与控制面板、底部导航的框架组合。落到鸿蒙(HarmonyOS/OpenHarmony)场景时,关键并不在“动画能不能跑”,而在“动画、布局与占位结构如何在 ArkUI 映射下保持稳定、低成本且一致”的工程约束。

设计动机与占位语义

骨架屏的目标是用“形状接近真实内容”的低对比占位快速建立视觉结构,缩短用户感知的等待时间,并避免布局跳动。示例把通用骨架抽象成 Skeleton,并通过 SkeletonCard/SkeletonListItem 组合出卡片型与列表型占位,这种“原子组件 + 组合组件”的方式能让同构视图(真实内容与占位)在三端保持统一结构,避免 ArkUI 渲染管线因布局变化造成的重排。

动画实现与 ArkUI 映射

占位动画采用 Animated.Value + Animated.loop + Animated.sequence,在 opacity 维度上做 0.5 ↔ 1 的往返淡入淡出,并启用 useNativeDriver。几个要点在跨端表现上尤为关键:

  • opacity/transform 动画是适合 useNativeDriver 的维度,动画计算与合成下推到原生(ArkUI)管线,避免 JS 线程参与逐帧计算,鸿蒙设备上帧率更稳。
  • 切记不要对 width/height 等布局属性做动画(会触发布局重算与重排),示例只用静态宽高并在样式层渲染,这对三端渲染成本最低。
  • 清理生命周期很重要:loop 返回的动画实例在卸载时 stop,避免在鸿蒙设备上出现“幽灵动画”继续驱动原生层而造成内存与功耗异常。

结构与样式的跨端选择

占位元素统一背景色与圆角,骨架层与内容层的卡片/列表样式保持一致;阴影采用双栈统一策略(iOS 使用 shadow*,安卓/鸿蒙使用 elevation),使卡片层次在三端一致。骨架背景颜色(#e2e8f0)对比度适中,建议在深色主题或高色域屏上也保持可读性,避免骨架过亮抢夺注意力。ArkUI 侧对圆角与阴影渲染开销相对可控,但在密集骨架(大量列表项)时仍应优先“轻阴影 + 分隔线”而非深阴影。

列表占位与性能边界

示例在 ScrollView 中渲染多条占位项,数据量较小时足够;当占位项数量增多或需要无限列表的加载指示,建议迁移到 FlatList,并利用:

  • initialNumToRender 与 windowSize 控制窗口大小,鸿蒙设备上显著提升滚动帧率
  • removeClippedSubviews 裁剪不可见区域,降低内存占用
  • 稳定 key 与 memo 化子项,减少 JS→UI 桥接重绘

把占位与真实列表都统一用 FlatList,可让“骨架→真实内容”的切换更顺滑、成本更低。

状态切换与加载流程

isLoading 用 useState 控制占位与真实内容切换,refreshData 用 setTimeout 模拟两秒加载。真实工程中需要注意:

  • 异步竞态与取消:加载过程中用户可能返回或切页,需在卸载时取消 pending 的异步(定时器/网络请求),避免在鸿蒙设备上出现卸载后 setState 的警告与无意义重绘。
  • 并发与降级:多段并发加载时优先显示“关键区域骨架”,其他区域用空白或轻占位,减小绘制压力;网络失败时从骨架降级到重试/失败态。

控制面板

切换按钮与刷新按钮保证最小交互闭环;emoji 图标(🔄、⏳)是跨端最简资源策略,避免矢量库桥接与包体膨胀。为三端一致性,可补充按压动效(Pressable + Animated 做轻缩放),并为交互元素添加 accessibilityRole/Label,让鸿蒙、Android、iOS 的读屏都能正确朗读“开始加载/停止加载/刷新数据”。

安全区域

顶层 SafeAreaView 护住状态栏与异形屏;鸿蒙设备形态多样,建议配合 react-native-safe-area-context 做 inset 兜底,确保头部与底部导航在折叠屏/打孔屏下也不被系统 UI 遮挡。内容区使用 ScrollView,若占位与真实内容切换后出现高度显著变化,保持卡片尺寸与位置一致、避免“跳动”,提升感知稳定性。


在鸿蒙生态系统中,React Native应用的运行依托于一套精密的桥接机制。当SafeAreaView组件初始化时,实际上是在鸿蒙侧的ComponentContainer中创建了一个具备安全区域感知能力的ArkUI节点。这种设计巧妙地屏蔽了不同设备形态(如刘海屏、瀑布屏)带来的布局差异,确保了应用界面在各种鸿蒙设备上的显示一致性。

Dimensions API的使用充分体现了RN框架对多设备适配的深度考量。通过动态获取屏幕宽度(width),组件能够实现真正的响应式布局。在鸿蒙平台上,这一API被映射为对DisplayManager服务的调用,能够实时响应屏幕旋转、折叠等状态变化,为用户提供连续一致的操作体验。

Skeleton骨架屏组件的核心在于Animated动画系统的运用。useEffect钩子中创建的Animated.loop循环动画通过Animated.sequence序列化两个timing动画,实现了opacity属性在0.5到1之间的往复变化。这种闪烁效果在鸿蒙设备上由ArkUI渲染引擎直接接管,利用GPU加速确保动画的流畅性,避免了JavaScript线程阻塞带来的卡顿问题。

fadeAnim状态值的初始化采用了new Animated.Value(0.5)构造函数,这在鸿蒙JSC引擎中会被优化为原生动画节点的创建。useNativeDriver:true配置项使得动画计算完全在原生UI线程中进行,大幅提升了性能表现。animation.stop()在组件卸载时的清理操作确保了鸿蒙系统资源的有效回收,防止内存泄漏。

SkeletonListItem和SkeletonCard这两个复合型骨架屏组件通过组合基础Skeleton元素构建了复杂的占位布局。avatar、title、subtitle等不同尺寸的骨骼元素通过width和height属性的精确控制,在鸿蒙设备上呈现出细腻的视觉层次感。这种模块化设计理念使得骨架屏组件具有良好的复用性和扩展性。

SkeletonScreen容器组件通过loading状态的条件渲染实现了真实内容与骨架屏的无缝切换。在鸿蒙平台上,这种渲染模式触发了React Reconciler的差异化更新算法,能够精准识别出需要替换的DOM节点,最小化重排重绘操作。setTimeout模拟的数据加载过程体现了异步状态管理在跨端开发中的重要性。

refreshData刷新函数的设计展现了状态驱动UI更新的核心思想。setIsLoading(true)调用立即触发骨架屏显示,随后的延时操作模拟真实的网络请求场景。在鸿蒙分布式环境中,这种加载态管理机制能够有效提升用户感知体验,降低等待焦虑。

TouchableOpacity组件在鸿蒙平台上的实现经过了特殊优化,其内置的responder negotiation机制能够智能处理多点触控冲突。refresh图标按钮的点击反馈通过ArkUI的波纹效果呈现,为用户提供了直观的操作确认。

StyleSheet.create创建的样式对象在鸿蒙环境下会被编译为高效的原生样式表示。其中skeleton样式类通过backgroundColor:#e0e0e0和borderRadius:4的组合营造出柔和的视觉效果,opacity动画属性的变化使整个骨架屏呈现出自然的呼吸感。

contentContainer、statRow等布局容器采用Flexbox模型实现响应式排列,在鸿蒙ArkUI引擎中被优化为高效的约束布局计算。statItem统计项通过justifyContent:'center’和alignItems:'center’实现完美的居中对齐,展现出RN在跨平台布局方面的一致性优势。


类型化骨架屏组件系统

骨架屏应用展示了高效的类型化占位组件设计:

const Skeleton = ({ style, width, height }: { 
  style?: any; 
  width?: number | string; 
  height?: number | string 
}) => {
  const [fadeAnim] = useState(new Animated.Value(0.5));
  
  React.useEffect(() => {
    const animation = Animated.loop(
      Animated.sequence([
        Animated.timing(fadeAnim, {
          toValue: 1,
          duration: 800,
          useNativeDriver: true,
        }),
        Animated.timing(fadeAnim, {
          toValue: 0.5,
          duration: 800,
          useNativeDriver: true,
        }),
      ])
    );
    
    animation.start();
    
    return () => {
      animation.stop();
    };
  }, [fadeAnim]);

  return (
    <Animated.View
      style={[
        styles.skeleton,
        { width, height },
        style,
        { opacity: fadeAnim }
      ]}
    />
  );
};

这种类型化设计在骨架屏应用中具有重要的动画性能保障作用。通过明确的尺寸属性和动画控制,确保了占位组件的流畅性和一致性。在鸿蒙平台上,这种类型可以无缝转化为鸿蒙的分布式动画服务,实现跨设备的骨架屏同步显示。

智能动画循环机制

应用实现了精确的骨架屏动画控制:

const animation = Animated.loop(
  Animated.sequence([
    Animated.timing(fadeAnim, {
      toValue: 1,
      duration: 800,
      useNativeDriver: true,
    }),
    Animated.timing(fadeAnim, {
      toValue: 0.5,
      duration: 800,
      useNativeDriver: true,
    }),
  ])
);

这种动画控制机制在骨架屏应用中展现了强大的视觉流畅性。通过循环序列、精确时长和原生驱动,提供了平滑的加载指示效果。在跨平台开发中,需要特别注意动画性能和电池消耗。鸿蒙平台的原生动画引擎可以提供更高效的动画渲染和更低的系统资源消耗。

占位布局与组件复用

复合骨架屏组件

应用采用了模块化的骨架屏组件设计:

const SkeletonListItem = () => (
  <View style={styles.skeletonItem}>
    <Skeleton style={styles.skeletonAvatar} width={50} height={50} />
    <View style={styles.skeletonTextContainer}>
      <Skeleton style={styles.skeletonTitle} width="70%" height={16} />
      <Skeleton style={styles.skeletonSubtitle} width="50%" height={14} />
    </View>
    <Skeleton style={styles.skeletonAction} width={60} height={30} />
  </View>
);

这种组件设计在骨架屏应用中展现了重要的布局复用性。通过原子化组件、弹性尺寸和一致的样式,提供了可复用的占位模板。在鸿蒙平台上,这种设计可以利用鸿蒙的组件复用机制实现更高效的占位渲染和更一致的视觉体验。

多状态内容切换

代码实现了智能的内容显示切换:

const SkeletonScreen = ({ loading }: { loading: boolean }) => {
  if (!loading) {
    return (
      // 实际内容显示
    );
  }
  
  return (
    // 骨架屏占位
  );
};

这种状态切换在加载优化中展现了强大的用户体验保障。通过条件渲染、平滑过渡和内容保持,提供了无闪烁的加载体验。在跨平台开发中,需要特别注意状态同步和过渡动画。鸿蒙平台的分布式状态管理可以提供更一致的多设备加载状态。

鸿蒙跨端适配关键技术

分布式加载状态同步

鸿蒙的分布式特性为加载体验带来创新:

// 伪代码:分布式加载同步
const DistributedLoading = {
  syncLoadingState: (loadingState) => {
    if (Platform.OS === 'harmony') {
      harmonyNative.syncSkeletonState(loadingState);
    }
  },
  getCrossDeviceLoading: () => {
    if (Platform.OS === 'harmony') {
      return harmonyNative.getUnifiedLoading();
    }
    return localLoading;
  }
};

原生动画优化

利用鸿蒙的原生动画能力提升体验:

// 伪代码:动画优化
const AnimationOptimization = {
  useNativeAnimations: () => {
    if (Platform.OS === 'harmony') {
      harmonyNative.accelerateSkeletonAnim();
    }
  },
  optimizeMemoryUsage: () => {
    if (Platform.OS === 'harmony') {
      harmonyNative.reduceSkeletonMemory();
    }
  }
};

智能加载预测

鸿蒙平台为骨架屏应用提供智能预加载能力:

// 伪代码:智能预加载
const IntelligentPreloading = {
  predictContentLoading: () => {
    if (Platform.OS === 'harmony') {
      harmonyNative.anticipateContentReady();
    }
  },
  adjustSkeletonTiming: () => {
    if (Platform.OS === 'harmony') {
      harmonyNative.optimizeSkeletonDuration();
    }
  }
};

性能优化与用户体验

骨架屏性能优化

// 伪代码:性能优化
const SkeletonPerformance = {
  optimizeRendering: () => {
    if (Platform.OS === 'harmony') {
      harmonyNative.accelerateSkeletonRender();
    }
  },
  minimizeAnimationImpact: () => {
    if (Platform.OS === 'harmony') {
      harmonyNative.reduceAnimBatteryDrain();
    }
  }
};

智能化骨架屏

// 伪代码:智能骨架屏
const IntelligentSkeleton = {
  adaptiveSkeletonLayout: () => {
    if (Platform.OS === 'harmony') {
      harmonyNative.adaptToContentStructure();
    }
  },
  predictContentPatterns: () => {
    if (Platform.OS === 'harmony') {
      harmonyNative.learnContentShapes();
    }
  }
};

渐进式加载增强

// 伪代码:渐进加载
const ProgressiveLoading = {
  enablePartialRendering: () => {
    if (Platform.OS === 'harmony') {
      harmonyNative.renderContentIncrementally();
    }
  },
  supportPriorityLoading: () => {
    if (Platform.OS === 'harmony') {
      harmonyNative.loadCriticalFirst();
    }
  }
};

真实演示案例代码:

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

// 图标库
const ICONS = {
  home: '🏠',
  list: '📋',
  loading: '⏳',
  refresh: '🔄',
  star: '⭐',
  heart: '❤️',
  user: '👤',
  check: '✅',
};

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

// 骨架屏组件
const Skeleton = ({ style, width, height }: { style?: any; width?: number | string; height?: number | string }) => {
  const [fadeAnim] = useState(new Animated.Value(0.5));
  
  React.useEffect(() => {
    const animation = Animated.loop(
      Animated.sequence([
        Animated.timing(fadeAnim, {
          toValue: 1,
          duration: 800,
          useNativeDriver: true,
        }),
        Animated.timing(fadeAnim, {
          toValue: 0.5,
          duration: 800,
          useNativeDriver: true,
        }),
      ])
    );
    
    animation.start();
    
    return () => {
      animation.stop();
    };
  }, [fadeAnim]);

  return (
    <Animated.View
      style={[
        styles.skeleton,
        { width, height },
        style,
        { opacity: fadeAnim }
      ]}
    />
  );
};

// 骨架屏列表项
const SkeletonListItem = () => (
  <View style={styles.skeletonItem}>
    <Skeleton style={styles.skeletonAvatar} width={50} height={50} />
    <View style={styles.skeletonTextContainer}>
      <Skeleton style={styles.skeletonTitle} width="70%" height={16} />
      <Skeleton style={styles.skeletonSubtitle} width="50%" height={14} />
    </View>
    <Skeleton style={styles.skeletonAction} width={60} height={30} />
  </View>
);

// 骨架屏卡片
const SkeletonCard = () => (
  <View style={styles.skeletonCard}>
    <Skeleton style={styles.skeletonImage} width="100%" height={150} />
    <View style={styles.skeletonCardContent}>
      <Skeleton style={styles.skeletonCardTitle} width="80%" height={20} />
      <Skeleton style={styles.skeletonCardSubtitle} width="100%" height={14} />
      <Skeleton style={styles.skeletonCardSubtitle} width="60%" height={14} />
      <Skeleton style={styles.skeletonCardButton} width={80} height={30} />
    </View>
  </View>
);

const SkeletonScreen = ({ loading }: { loading: boolean }) => {
  if (!loading) {
    return (
      <View style={styles.contentContainer}>
        <View style={styles.contentCard}>
          <Text style={styles.contentTitle}>用户信息</Text>
          <Text style={styles.contentText}>姓名: 张三</Text>
          <Text style={styles.contentText}>邮箱: zhangsan@example.com</Text>
          <Text style={styles.contentText}>职位: 软件工程师</Text>
        </View>
        
        <View style={styles.contentList}>
          <Text style={styles.contentTitle}>最近任务</Text>
          <View style={styles.contentItem}>
            <Text style={styles.contentItemText}>✓ 完成项目需求分析</Text>
          </View>
          <View style={styles.contentItem}>
            <Text style={styles.contentItemText}>✓ 设计数据库结构</Text>
          </View>
          <View style={styles.contentItem}>
            <Text style={styles.contentItemText}>⚠️ 实现用户认证功能</Text>
          </View>
        </View>
        
        <View style={styles.contentCard}>
          <Text style={styles.contentTitle}>项目统计</Text>
          <View style={styles.statRow}>
            <View style={styles.statItem}>
              <Text style={styles.statNumber}>24</Text>
              <Text style={styles.statLabel}>已完成</Text>
            </View>
            <View style={styles.statItem}>
              <Text style={styles.statNumber}>6</Text>
              <Text style={styles.statLabel}>进行中</Text>
            </View>
            <View style={styles.statItem}>
              <Text style={styles.statNumber}>3</Text>
              <Text style={styles.statLabel}>待开始</Text>
            </View>
          </View>
        </View>
      </View>
    );
  }
  
  return (
    <View style={styles.contentContainer}>
      <SkeletonCard />
      <SkeletonListItem />
      <SkeletonListItem />
      <SkeletonListItem />
      <View style={styles.skeletonStats}>
        <Skeleton style={styles.skeletonStat} width={80} height={60} />
        <Skeleton style={styles.skeletonStat} width={80} height={60} />
        <Skeleton style={styles.skeletonStat} width={80} height={60} />
      </View>
    </View>
  );
};

const SkeletonDemoApp: React.FC = () => {
  const [isLoading, setIsLoading] = useState(true);
  
  const refreshData = () => {
    setIsLoading(true);
    // 模拟数据加载
    setTimeout(() => {
      setIsLoading(false);
    }, 2000);
  };

  return (
    <SafeAreaView style={styles.container}>
      {/* 头部 */}
      <View style={styles.header}>
        <Text style={styles.title}>骨架屏组件</Text>
        <Text style={styles.subtitle}>加载数据时的占位组件</Text>
      </View>

      {/* 控制面板 */}
      <View style={styles.controlPanel}>
        <TouchableOpacity 
          style={styles.controlButton}
          onPress={() => setIsLoading(!isLoading)}
        >
          <Text style={styles.controlButtonText}>
            {isLoading ? '停止加载' : '开始加载'}
          </Text>
        </TouchableOpacity>
        
        <TouchableOpacity 
          style={styles.refreshButton}
          onPress={refreshData}
        >
          <Text style={styles.refreshButtonText}>{ICONS.refresh} 刷新数据</Text>
        </TouchableOpacity>
      </View>

      {/* 内容区域 */}
      <ScrollView style={styles.content}>
        <SkeletonScreen loading={isLoading} />
      </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.list}</Text>
          <Text style={styles.navText}>列表</Text>
        </TouchableOpacity>
        <TouchableOpacity style={styles.navItem}>
          <Text style={styles.navIcon}>{ICONS.loading}</Text>
          <Text style={styles.navText}>加载</Text>
        </TouchableOpacity>
        <TouchableOpacity style={styles.navItem}>
          <Text style={styles.navIcon}>{ICONS.user}</Text>
          <Text style={styles.navText}>我的</Text>
        </TouchableOpacity>
      </View>
    </SafeAreaView>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#f8fafc',
  },
  header: {
    padding: 20,
    backgroundColor: '#ffffff',
    borderBottomWidth: 1,
    borderBottomColor: '#e2e8f0',
  },
  title: {
    fontSize: 24,
    fontWeight: 'bold',
    color: '#1e293b',
    marginBottom: 4,
  },
  subtitle: {
    fontSize: 14,
    color: '#64748b',
  },
  controlPanel: {
    flexDirection: 'row',
    justifyContent: 'space-around',
    padding: 16,
    backgroundColor: '#ffffff',
    margin: 16,
    borderRadius: 12,
    elevation: 3,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.1,
    shadowRadius: 4,
  },
  controlButton: {
    backgroundColor: '#3b82f6',
    paddingHorizontal: 16,
    paddingVertical: 10,
    borderRadius: 8,
    alignItems: 'center',
    flex: 1,
    marginRight: 8,
  },
  refreshButton: {
    backgroundColor: '#10b981',
    paddingHorizontal: 16,
    paddingVertical: 10,
    borderRadius: 8,
    alignItems: 'center',
    flex: 1,
    marginLeft: 8,
  },
  controlButtonText: {
    fontSize: 14,
    color: '#ffffff',
    fontWeight: '500',
  },
  refreshButtonText: {
    fontSize: 14,
    color: '#ffffff',
    fontWeight: '500',
  },
  content: {
    flex: 1,
    padding: 16,
  },
  contentContainer: {
    flex: 1,
  },
  contentCard: {
    backgroundColor: '#ffffff',
    padding: 16,
    borderRadius: 12,
    marginBottom: 16,
    elevation: 2,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 1 },
    shadowOpacity: 0.1,
    shadowRadius: 2,
  },
  contentTitle: {
    fontSize: 18,
    fontWeight: 'bold',
    color: '#1e293b',
    marginBottom: 12,
  },
  contentText: {
    fontSize: 16,
    color: '#64748b',
    marginBottom: 8,
  },
  contentList: {
    backgroundColor: '#ffffff',
    padding: 16,
    borderRadius: 12,
    marginBottom: 16,
    elevation: 2,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 1 },
    shadowOpacity: 0.1,
    shadowRadius: 2,
  },
  contentItem: {
    paddingVertical: 8,
    borderBottomWidth: 1,
    borderBottomColor: '#f1f5f9',
  },
  contentItemText: {
    fontSize: 16,
    color: '#1e293b',
  },
  statRow: {
    flexDirection: 'row',
    justifyContent: 'space-between',
  },
  statItem: {
    alignItems: 'center',
    flex: 1,
  },
  statNumber: {
    fontSize: 20,
    fontWeight: 'bold',
    color: '#3b82f6',
  },
  statLabel: {
    fontSize: 12,
    color: '#64748b',
    marginTop: 4,
  },
  skeleton: {
    backgroundColor: '#e2e8f0',
    borderRadius: 4,
  },
  skeletonItem: {
    flexDirection: 'row',
    alignItems: 'center',
    backgroundColor: '#ffffff',
    padding: 16,
    borderRadius: 12,
    marginBottom: 12,
    elevation: 2,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 1 },
    shadowOpacity: 0.1,
    shadowRadius: 2,
  },
  skeletonAvatar: {
    borderRadius: 25,
    marginRight: 12,
  },
  skeletonTextContainer: {
    flex: 1,
    justifyContent: 'center',
  },
  skeletonTitle: {
    marginBottom: 8,
  },
  skeletonSubtitle: {
    marginBottom: 4,
  },
  skeletonAction: {
    borderRadius: 8,
  },
  skeletonCard: {
    backgroundColor: '#ffffff',
    borderRadius: 12,
    marginBottom: 16,
    overflow: 'hidden',
    elevation: 2,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 1 },
    shadowOpacity: 0.1,
    shadowRadius: 2,
  },
  skeletonImage: {
    backgroundColor: '#e2e8f0',
  },
  skeletonCardContent: {
    padding: 16,
  },
  skeletonCardTitle: {
    marginBottom: 12,
  },
  skeletonCardSubtitle: {
    marginBottom: 8,
  },
  skeletonCardButton: {
    borderRadius: 8,
    marginTop: 12,
    alignSelf: 'flex-start',
  },
  skeletonStats: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    marginBottom: 16,
  },
  skeletonStat: {
    borderRadius: 8,
  },
  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 SkeletonDemoApp;

请添加图片描述


打包

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

在这里插入图片描述

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

在这里插入图片描述

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

请添加图片描述

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

Logo

鲲鹏昇腾开发者社区是面向全社会开放的“联接全球计算开发者,聚合华为+生态”的社区,内容涵盖鲲鹏、昇腾资源,帮助开发者快速获取所需的知识、经验、软件、工具、算力,支撑开发者易学、好用、成功,成为核心开发者。

更多推荐