HarmonyOS APP开发---"好物优选"电商导购App,需要用到这个库
做一个电商导购 App,首页→分类列表→商品详情→购物车→结算,页面栈深度 5+。不开原生屏幕的话,所有页面都挂载在内存里------
@react-native-ohos/react-native-screens一行开启,不可见页面真正移除渲染树,内存直接降一半。
📦 仓库地址:https://gitcode.com/CPF-RN/rntpc_react-native-screens | 安装:npm install @react-native-ohos/react-native-screens
写在前面
"好物优选"是一个电商导购 App,典型用户路径:
首页 → 分类页 → 商品列表 → 商品详情 → 购物车 → 结算 → 订单详情
一条路径走下来,页面栈深度可达 5-7 层。如果用 react-navigation 默认的 JS 视图栈模拟,所有 7 个页面的 UI 组件都挂载在视图树里------即使你看不到它们,它们也在内存里占着位置,低端鸿蒙设备上直接 OOM。
更严重的是切换卡顿:JS 层动画受线程影响,商品详情页这种重页面 push 时明显掉帧。
react-native-screens 把屏幕管理下沉到鸿蒙原生层------不可见页面从渲染树 detach(保留状态),切换动画在原生层执行 。enableScreens(true) 一行开启,现有代码零改动。
这篇文章聊什么
- 开启原生屏幕------一行配置,导航性能直接提升
- 页面栈深度优化------7 层页面不再全部挂载内存
- Tab 冻结------非活跃 Tab 页面冻结渲染
#mermaid-svg-9UE89NloA4muaq6N{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-9UE89NloA4muaq6N .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-9UE89NloA4muaq6N .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-9UE89NloA4muaq6N .error-icon{fill:#552222;}#mermaid-svg-9UE89NloA4muaq6N .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-9UE89NloA4muaq6N .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-9UE89NloA4muaq6N .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-9UE89NloA4muaq6N .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-9UE89NloA4muaq6N .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-9UE89NloA4muaq6N .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-9UE89NloA4muaq6N .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-9UE89NloA4muaq6N .marker{fill:#333333;stroke:#333333;}#mermaid-svg-9UE89NloA4muaq6N .marker.cross{stroke:#333333;}#mermaid-svg-9UE89NloA4muaq6N svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-9UE89NloA4muaq6N p{margin:0;}#mermaid-svg-9UE89NloA4muaq6N .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-9UE89NloA4muaq6N .cluster-label text{fill:#333;}#mermaid-svg-9UE89NloA4muaq6N .cluster-label span{color:#333;}#mermaid-svg-9UE89NloA4muaq6N .cluster-label span p{background-color:transparent;}#mermaid-svg-9UE89NloA4muaq6N .label text,#mermaid-svg-9UE89NloA4muaq6N span{fill:#333;color:#333;}#mermaid-svg-9UE89NloA4muaq6N .node rect,#mermaid-svg-9UE89NloA4muaq6N .node circle,#mermaid-svg-9UE89NloA4muaq6N .node ellipse,#mermaid-svg-9UE89NloA4muaq6N .node polygon,#mermaid-svg-9UE89NloA4muaq6N .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-9UE89NloA4muaq6N .rough-node .label text,#mermaid-svg-9UE89NloA4muaq6N .node .label text,#mermaid-svg-9UE89NloA4muaq6N .image-shape .label,#mermaid-svg-9UE89NloA4muaq6N .icon-shape .label{text-anchor:middle;}#mermaid-svg-9UE89NloA4muaq6N .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-9UE89NloA4muaq6N .rough-node .label,#mermaid-svg-9UE89NloA4muaq6N .node .label,#mermaid-svg-9UE89NloA4muaq6N .image-shape .label,#mermaid-svg-9UE89NloA4muaq6N .icon-shape .label{text-align:center;}#mermaid-svg-9UE89NloA4muaq6N .node.clickable{cursor:pointer;}#mermaid-svg-9UE89NloA4muaq6N .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-9UE89NloA4muaq6N .arrowheadPath{fill:#333333;}#mermaid-svg-9UE89NloA4muaq6N .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-9UE89NloA4muaq6N .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-9UE89NloA4muaq6N .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9UE89NloA4muaq6N .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-9UE89NloA4muaq6N .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9UE89NloA4muaq6N .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-9UE89NloA4muaq6N .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-9UE89NloA4muaq6N .cluster text{fill:#333;}#mermaid-svg-9UE89NloA4muaq6N .cluster span{color:#333;}#mermaid-svg-9UE89NloA4muaq6N div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-9UE89NloA4muaq6N .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-9UE89NloA4muaq6N rect.text{fill:none;stroke-width:0;}#mermaid-svg-9UE89NloA4muaq6N .icon-shape,#mermaid-svg-9UE89NloA4muaq6N .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9UE89NloA4muaq6N .icon-shape p,#mermaid-svg-9UE89NloA4muaq6N .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-9UE89NloA4muaq6N .icon-shape .label rect,#mermaid-svg-9UE89NloA4muaq6N .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9UE89NloA4muaq6N .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-9UE89NloA4muaq6N .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-9UE89NloA4muaq6N :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} push
push
push
push
push
push
不可见页面
首页
分类页
商品列表
商品详情
购物车
结算
订单详情
原生层 detach
内存只保留可见页
状态保留, 返回即恢复
第一步:安装与开启
bash
npm install @react-native-ohos/react-native-screens
在 App 入口开启原生屏幕:
tsx
import { enableScreens } from '@react-native-ohos/react-native-screens'
enableScreens(true) // 一行开启,全局生效
第二步:配置导航栈
用 createNativeStackNavigator 替代 createStackNavigator,底层自动走原生屏幕:
tsx
import { NavigationContainer } from '@react-navigation/native'
import { createNativeStackNavigator } from '@react-navigation/native-stack'
const Stack = createNativeStackNavigator()
function App() {
return (
<NavigationContainer>
<Stack.Navigator screenOptions={{ headerShown: false }}>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Category" component={CategoryScreen} />
<Stack.Screen name="ProductList" component={ProductListScreen} />
<Stack.Screen name="ProductDetail" component={ProductDetailScreen} />
<Stack.Screen name="Cart" component={CartScreen} />
<Stack.Screen name="Checkout" component={CheckoutScreen} />
<Stack.Screen name="OrderDetail" component={OrderDetailScreen} />
</Stack.Navigator>
</NavigationContainer>
)
}
API 和普通 StackNavigator 完全一致 ------navigation.push / navigation.navigate / navigation.goBack 照常调用,开发者无感知。但底层行为变了:
| 对比 | JS StackNavigator | Native Stack (screens) |
|---|---|---|
| 7 层页面内存 | 全部挂载 | 只保留可见页 |
| push 动画 | JS 线程,掉帧 | 原生层,60fps |
| 返回恢复 | 重新渲染 | 状态已保留 |
| 生命周期 | 模糊 | attach/detach 精确 |
第三步:Tab 冻结------非活跃 Tab 不渲染
底部有 5 个 Tab(首页/分类/购物车/消息/我的),切到首页时其他 4 个 Tab 不该继续渲染:
tsx
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'
const Tab = createBottomTabNavigator()
<Tab.Navigator
screenOptions={{
// 非活跃 Tab 冻结渲染,保留状态
freezeOnBlur: true,
}}
>
<Tab.Screen name="Home" component={HomeTab} />
<Tab.Screen name="Category" component={CategoryTab} />
<Tab.Screen name="Cart" component={CartTab} />
<Tab.Screen name="Message" component={MessageTab} />
<Tab.Screen name="Profile" component={ProfileTab} />
</Tab.Navigator>
freezeOnBlur: true 让切走的 Tab 停止渲染但不销毁状态------用户切回来时数据还在,但切走期间不占 GPU 资源。这对"好物优选"的购物车 Tab 特别重要------购物车里有商品列表 + 倒计时,不冻结的话一直在后台跑。
第四步:页面切换动画优化
商品详情页很重(大量图片 + 评论列表),push 时容易卡。原生屏幕的转场动画在原生层执行:
tsx
<Stack.Screen
name="ProductDetail"
component={ProductDetailScreen}
options={{
// 原生层转场动画,不受 JS 线程影响
animation: 'slide_from_right',
animationDuration: 300,
// 透明背景过渡,减少闪烁
presentation: 'card',
}}
/>
animation: 'slide_from_right' 在原生层执行------即使 JS 线程在忙(比如图片解码),push 动画也丝滑不卡。
为什么"好物优选"选了 Screens?
| 需求 | 不开 screens | 开启 screens |
|---|---|---|
| 7 层页面内存 | 全部挂载 ~200MB | 只留可见页 ~60MB |
| Tab 切换 | 全部渲染 | ✅ freezeOnBlur |
| push 动画 | JS 线程掉帧 | ✅ 原生层 60fps |
| 返回恢复 | 重新渲染 | ✅ 状态保留 |
| 代码改动 | --- | ✅ 一行 enableScreens |
总结
"好物优选"这个场景里,@react-native-ohos/react-native-screens 解决了三件事:
- 内存不炸------7 层页面栈只保留可见页,不可见页原生层 detach
- Tab 不浪费 ------
freezeOnBlur让非活跃 Tab 停止渲染 - 切换不卡------push/pop 动画在原生层,重页面也不掉帧
如果你也在做多页面导航的 RN 鸿蒙应用(电商、社交、工具类),react-native-screens 是 enableScreens(true) 一行就能收获性能提升的必选基础设施。