🚀【RN鸿蒙教学|第6课时】页面跳转+参数传递(列表→详情页)+ 鸿蒙多终端适配✨

适配版本:RN 0.72.7 + OpenHarmony SDK 8.0 + @react-navigation/bottom-tabs@6.5.7 + @react-navigation/stack@6.3.16
学习时长:90分钟
难度等级:⭐⭐⭐(进阶)
在这里插入图片描述

📋 目录导航

  1. 适合人群 & 课时目标
  2. 课前准备
  3. 核心知识点讲解
  4. 实操步骤(核心环节)
  5. 常见问题与解决方案
  6. 课堂小结
  7. 课后任务(必做)
  8. 核心要点总结

哈喽大家好~👋 欢迎来到React Native(RN)兼容开源鸿蒙(OpenHarmony)跨平台开发系列教学第6课时!🎓

上一课时我们完成了底部选项卡(Tab导航)的开发,拆分了首页、我的页面,实现了多页面无跳转式切换,完善了应用的核心页面结构🧱。本节课我们将进入页面交互的关键环节——页面跳转与参数传递,核心实现“首页列表项点击→详情页”的跳转,同时掌握正向、反向参数传递的方法,解决鸿蒙多终端跳转适配问题,让应用形成完整的页面交互闭环🔄。

本课时核心目标是帮大家掌握RN鸿蒙工程中页面跳转的两种核心方式(三方库导航跳转+原生跳转),熟练实现参数正向传递(列表→详情)与反向传递(详情→首页),开发详情页并适配多终端,同时巩固组件复用、状态管理和Git代码提交规范,为后续表单开发、数据持久化做好铺垫。


🎯 适合人群 & 课时目标

适合人群

已完成前5课时实操,掌握底部Tab导航、列表渲染、多页面拆分,想要学习页面跳转、参数传递的开发者👨💻👩💻

课时目标(90分钟达成)

  1. ✨ 熟练掌握@react-navigation/bottom-tabs三方库的页面跳转用法(适配鸿蒙),实现列表项点击跳转详情页;
  2. 📤📥 掌握页面参数正向传递(列表→详情,携带用户信息)与反向传递(详情→首页,返回操作结果)的核心逻辑;
  3. 🎨 开发用户详情页,复用网络请求逻辑,适配鸿蒙多终端屏幕尺寸和显示效果;
  4. 🐞 解决页面跳转常见问题(跳转失败、参数接收不到、反向传参异常、开发板跳转卡顿);
  5. ✅ 完成跳转功能开发、多终端适配与Git规范提交,确保应用交互流畅、功能完整。

🔧 一、课前准备(5分钟)

提前做好以下准备,确保课时内高效实操,无缝衔接上一课时内容,避免卡顿,快速进入核心开发:

  • ✅ 确认第5课时完成的RN鸿蒙工程(rnHarmonyDemo)可正常运行,底部Tab导航(首页/我的)切换无异常,首页列表可正常渲染、交互:
    cd rnHarmonyDemo
    react-native run-ohos --emulator # 验证工程运行💻
    
  • ✅ 切换到规范功能分支(推荐新建feature-page-navigation分支):
    # 从feature-tab-navigation分支创建新分支
    git checkout feature-tab-navigation
    git checkout -b feature-page-navigation
    git branch # 验证当前分支🔍
    
  • ✅ 预习@react-navigation导航跳转相关用法(navigation.navigate)、参数传递基础(route.params),了解页面跳转的生命周期变化📚;
  • ✅ 回顾第3课时的网络请求逻辑(Axios),确认用户列表接口可正常获取数据(详情页需复用用户ID获取详情数据)📡;
  • ✅ 打开DevEco Studio、VScode(加载RN工程)、Git Bash,确认所有工具可正常使用,多终端调试环境正常,RN版本保持0.72.7,三方库版本与上一课时一致🖥️。

⚠️ 关键注意:

  1. 重点确认@react-navigation/bottom-tabs三方库已正常集成,首页列表可正常点击;若Tab导航或列表有异常,先回顾第4、5课时的问题排查环节,优先解决基础问题;
  2. 确保参数传递时数据格式统一,避免出现类型错误(如传递数字ID而非字符串)🚨。

📚 二、核心知识点讲解(15分钟)

2.1 页面跳转的核心逻辑与鸿蒙适配要点(重点⭐)

页面跳转是跨平台应用的核心交互之一,RN鸿蒙工程中,优先使用@react-navigation系列三方库实现跳转(适配性更强,无需手动处理鸿蒙手势差异),核心逻辑与适配要点如下:

核心跳转逻辑
2. 步骤拆解(逐环节解释)
环节 具体操作 核心代码示例 作用说明
🎯 触发跳转 点击首页列表项 onPress={() => navigation.navigate('Detail', { userId: item.id })} 触发跳转事件,携带需要传递的参数(如用户ID)
🧭 调用导航方法 执行navigate方法 navigation.navigate('目标页面名称', { 参数字段: 值 }) 告诉导航容器需要跳转到指定名称的页面,并携带参数
🗺️ 路由解析 导航容器校验配置 提前在Stack.Navigator中注册Detail页面 确认目标页面已配置,避免跳转失败(鸿蒙多终端适配核心前提)
🎨 渲染页面 加载详情页组件 <Stack.Screen name="Detail" component={Detail} /> 根据路由配置渲染详情页组件,初始化页面状态
📥 接收参数 详情页获取参数 const { userId } = route.params ?? {} 从路由参数中提取跳转时携带的数据(做容错处理,避免参数缺失报错)
↩️ 返回首页 执行返回操作 navigation.goBack()navigation.navigate('HomePage') 从详情页返回首页,反向传参时可携带操作结果

总结

核心跳转逻辑的关键要点:

  1. 跳转的核心是navigation.navigate方法,需指定页面名称(与路由配置的name一致,大小写敏感)和参数字典
  2. 鸿蒙多终端适配中,需确保导航容器(NavigationContainer)包裹所有导航组件,开发板可关闭跳转动画提升性能;
  3. 参数接收必须做容错处理(route.params ?? {}),避免无参数时页面报错。
鸿蒙适配核心
适配维度 具体操作 适用终端
🎬 动画适配 简化跳转动画(禁用渐变/缩放,仅保留滑动) 开发板/低配真机
✋ 手势适配 真机兼容右滑返回,开发板添加明确返回按钮 真机/开发板
🗺️ 路由配置 所有跳转页面注册到导航容器,避免跳转失败 全终端
⚡ 性能优化 减少跳转时传递的数据量,简化详情页渲染 开发板
两种跳转方式对比(贴合鸿蒙开发场景)📊
跳转方式 优势 劣势 适用场景
🧭 导航跳转(@react-navigation) ✅ 自带路由管理、状态保留;✅ 适配鸿蒙多终端;✅ 开发效率高 ❌ 灵活度略低;❌ 需适配版本 绝大多数场景(列表→详情、页面间跳转)
🪟 原生跳转(Modal组件) ✅ 灵活度极高;✅ 无需依赖三方库 ❌ 需手动管理路由/状态;❌ 适配难度大 简单弹窗式跳转(如确认框、简易表单)

2.2 参数传递的两种核心方式(正向+反向)📤📥

页面跳转时,通常需要携带数据或返回操作结果,核心分为正向传递和反向传递,覆盖所有开发场景:

传递方式 用途 实现方式 鸿蒙适配注意
📤 正向传递(列表→详情) 携带跳转数据(如用户ID、名称) 跳转时传参:navigation.navigate('Detail', { userId: 1 });详情页接收:route.params?.userId ❗ 传递简单数据(字符串/数字/对象),避免复杂函数
📥 反向传递(详情→首页) 返回操作结果(如“标记已查看”) 详情页:navigation.navigate('HomePage', { isViewed: true });首页:useEffect监听route.params ❗ 做容错处理,兼容右滑返回场景

2.3 详情页开发的核心要点🧩

详情页是列表页面的延伸,开发时需兼顾数据请求、样式适配、交互体验,核心要点:

  1. 📡 数据请求:通过正向传递的用户ID调用详情接口,获取完整信息;
  2. 📱 样式适配:弹性布局+多终端尺寸判断(开发板增大字体/间距);
  3. 🖱️ 交互设计:添加返回按钮(开发板必备)、操作按钮(反向传参);
  4. 🛡️ 兜底处理:加载中/加载失败/参数缺失提示,避免白屏/报错。

2.4 鸿蒙多终端跳转适配差异🖥️

终端类型 适配调整 具体操作
💻 模拟器 基础适配 验证跳转/传参正常,支持右滑返回
📱 鸿蒙真机(手机/平板) 手势+布局适配 兼容右滑返回,平板端详情页内容分行展示
🖥️ DAYU200开发板 性能+交互适配 关闭跳转动画、增大点击区域、简化详情页样式

💻 三、实操步骤(50分钟,重点环节)

本环节全程实操,基于上一课时的Tab导航、首页列表,实现列表→详情页跳转(正向传参)、详情页开发、反向传参,完成多终端适配与代码提交,每一步都有验证环节,新手友好。

3.1 步骤1:准备详情页接口,创建详情页组件(10分钟)📄

1.1 详情页测试接口
GET https://jsonplaceholder.typicode.com/users/{userId}
# 示例:https://jsonplaceholder.typicode.com/users/1(获取ID为1的用户详情)
1.2 安装堆栈导航依赖(补充)
# 安装堆栈导航库(适配RN 0.72.7)
npm install @react-navigation/stack@6.3.16 --save
1.3 创建详情页组件(src/pages/Detail.js)
import React, { useState, useEffect } from 'react';
import { View, Text, TouchableOpacity, StyleSheet, Dimensions } from 'react-native';
import service from '../api/request';

// 📏 鸿蒙多终端适配:开发板判断
const { height } = Dimensions.get('window');
const isBoard = height < 600; // 开发板屏幕偏小

// 接收导航和路由参数
const Detail = ({ navigation, route }) => {
  const [userDetail, setUserDetail] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState('');
  
  // 接收正向传递的参数(容错处理)
  const { userId, userName } = route.params ?? {};

  // 请求用户详情数据
  useEffect(() => {
    // 容错:无userId时直接提示错误
    if (!userId) {
      setError('❌ 参数缺失,无法获取用户详情');
      setLoading(false);
      return;
    }

    // 异步请求详情数据
    const fetchUserDetail = async () => {
      try {
        const res = await service.get(`/users/${userId}`);
        setUserDetail(res);
        setLoading(false);
      } catch (err) {
        setError(`❌ 加载失败:${err.message}`);
        setLoading(false);
      }
    };

    fetchUserDetail();
  }, [userId]);

  return (
    <View style={styles.container}>
      {/* 返回按钮(开发板必备) */}
      <TouchableOpacity 
        style={styles.backBtn} 
        onPress={() => navigation.goBack()}
        activeOpacity={0.8}
      >
        <Text style={styles.backBtnText}>← 返回首页</Text>
      </TouchableOpacity>

      {/* 加载/错误/空状态 */}
      {loading ? (
        <Text style={[styles.loadingText, isBoard && { fontSize: 18 }]}>加载中...🔄</Text>
      ) : error ? (
        <Text style={[styles.errorText, isBoard && { fontSize: 18 }]}>{error}</Text>
      ) : !userDetail ? (
        <Text style={[styles.errorText, isBoard && { fontSize: 18 }]}>❌ 无用户详情数据</Text>
      ) : (
        <View style={styles.detailContainer}>
          {/* 用户名标题 */}
          <Text style={[styles.detailTitle, isBoard && { fontSize: 22 }]}>{userDetail.name}</Text>
          
          {/* 详情信息列表 */}
          <View style={styles.detailItem}>
            <Text style={styles.detailLabel}>📧 邮箱:</Text>
            <Text style={[styles.detailValue, isBoard && { fontSize: 17 }]}>{userDetail.email}</Text>
          </View>
          
          <View style={styles.detailItem}>
            <Text style={styles.detailLabel}>📱 电话:</Text>
            <Text style={[styles.detailValue, isBoard && { fontSize: 17 }]}>{userDetail.phone}</Text>
          </View>
          
          <View style={styles.detailItem}>
            <Text style={styles.detailLabel}>📍 地址:</Text>
            <Text style={[styles.detailValue, isBoard && { fontSize: 17 }]}>
              {userDetail.address.street} {userDetail.address.city}
            </Text>
          </View>
          
          <View style={styles.detailItem}>
            <Text style={styles.detailLabel}>🏢 公司:</Text>
            <Text style={[styles.detailValue, isBoard && { fontSize: 17 }]}>{userDetail.company.name}</Text>
          </View>
          
          <View style={styles.detailItem}>
            <Text style={styles.detailLabel}>🌐 网站:</Text>
            <Text style={[styles.detailValue, isBoard && { fontSize: 17 }]}>{userDetail.website}</Text>
          </View>

          {/* 反向传参按钮(标记已查看) */}
          <TouchableOpacity
            style={[styles.operateBtn, isBoard && { padding: 15 }]}
            onPress={() => {
              // 反向传递参数:标记已查看
              navigation.navigate('HomePage', {
                isViewed: true,
                viewedUserId: userId
              });
              // 返回首页
              navigation.goBack();
            }}
            activeOpacity={0.8}
          >
            <Text style={[styles.operateBtnText, isBoard && { fontSize: 18 }]}>✅ 标记为已查看</Text>
          </TouchableOpacity>
        </View>
      )}
    </View>
  );
};

// 样式(适配鸿蒙多终端)
const styles = StyleSheet.create({
  container: { 
    flex: 1, 
    padding: isBoard ? 20 : 15, 
    backgroundColor: '#fff' 
  },
  backBtn: { 
    marginBottom: isBoard ? 25 : 20 
  },
  backBtnText: { 
    fontSize: isBoard ? 18 : 16, 
    color: '#007AFF' 
  },
  loadingText: { 
    textAlign: 'center', 
    marginTop: 50, 
    fontSize: 16 
  },
  errorText: { 
    textAlign: 'center', 
    marginTop: 50, 
    fontSize: 16, 
    color: '#ff4444' 
  },
  detailContainer: { 
    gap: isBoard ? 20 : 15 
  },
  detailTitle: { 
    fontSize: 20, 
    fontWeight: 'bold', 
    textAlign: 'center', 
    marginBottom: 15 
  },
  detailItem: { 
    flexDirection: 'row', 
    gap: 8, 
    flexWrap: 'wrap' // 换行适配小屏幕
  },
  detailLabel: { 
    fontSize: 16, 
    color: '#333', 
    fontWeight: '500' 
  },
  detailValue: { 
    fontSize: 16, 
    color: '#666', 
    flex: 1 // 自适应剩余空间
  },
  operateBtn: { 
    marginTop: 20, 
    backgroundColor: '#007AFF', 
    padding: 12, 
    borderRadius: 8, 
    alignItems: 'center' 
  },
  operateBtnText: { 
    color: 'white', 
    fontSize: 16 
  }
});

export default Detail;
1.4 配置嵌套路由(修改App.js)

将Tab导航与堆栈导航嵌套,实现“Tab切换+页面跳转”:

// App.js(完整配置)
import React from 'react';
import { Text, Dimensions } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { createStackNavigator } from '@react-navigation/stack'; // 新增:堆栈导航
import Home from './src/pages/Home';
import Mine from './src/pages/Mine';
import Detail from './src/pages/Detail'; // 新增:导入详情页

// 📏 鸿蒙多终端适配
const { height } = Dimensions.get('window');
const isBoard = height < 600;

// 创建导航器
const Tab = createBottomTabNavigator();
const Stack = createStackNavigator(); // 新增:堆栈导航器

// 首页堆栈(首页 + 详情页)
const HomeStack = () => (
  <Stack.Navigator 
    screenOptions={{ 
      headerShown: false,
      animationEnabled: !isBoard // 开发板关闭跳转动画,提升性能
    }}
  >
    <Stack.Screen name="HomePage" component={Home} /> {/* 首页 */}
    <Stack.Screen name="Detail" component={Detail} /> {/* 详情页 */}
  </Stack.Navigator>
);

const App = () => {
  return (
    <NavigationContainer>
      <Tab.Navigator
        screenOptions={{
          tabBarActiveTintColor: '#007AFF',
          tabBarInactiveTintColor: '#666',
          tabBarStyle: {
            height: isBoard ? 60 : 50,
            paddingBottom: isBoard ? 5 : 2,
            paddingTop: isBoard ? 5 : 2,
            borderTopWidth: 1,
            borderTopColor: '#eee',
            ...(isBoard && { shadowOpacity: 0, elevation: 0 })
          },
          tabBarItemStyle: {
            padding: isBoard ? 10 : 5
          },
          headerShown: false,
          animationEnabled: !isBoard
        }}
      >
        {/* 首页Tab(替换为堆栈导航) */}
        <Tab.Screen
          name="Home"
          component={HomeStack} // 关键:使用HomeStack而非直接Home
          options={{
            title: '首页',
            tabBarIcon: ({ focused }) => (
              <Text style={{ fontSize: isBoard ? 20 : 18, color: focused ? '#007AFF' : '#666' }}>🏠</Text>
            ),
            tabBarLabelStyle: { fontSize: isBoard ? 14 : 12 }
          }}
        />
        {/* 我的页面Tab */}
        <Tab.Screen
          name="Mine"
          component={Mine}
          options={{
            title: '我的',
            tabBarIcon: ({ focused }) => (
              <Text style={{ fontSize: isBoard ? 20 : 18, color: focused ? '#007AFF' : '#666' }}>👤</Text>
            ),
            tabBarLabelStyle: { fontSize: isBoard ? 14 : 12 }
          }}
        />
      </Tab.Navigator>
    </NavigationContainer>
  );
};

export default App;

3.2 步骤2:实现列表项点击跳转,正向传递参数(10分钟)🔗

修改首页列表项,添加点击事件,实现跳转并携带参数:

// src/pages/Home.js(核心修改)
import React, { useState, useEffect } from 'react';
import { View, Text, StyleSheet, FlatList, RefreshControl, TouchableOpacity, Dimensions } from 'react-native';
import service from '../api/request';

const { height } = Dimensions.get('window');
const isBoard = height < 600;

// 新增:接收navigation参数
const Home = ({ navigation, route }) => {
  // 原有状态不变
  const [userList, setUserList] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState('');
  const [page, setPage] = useState(1);
  const [limit, setLimit] = useState(5);
  const [hasMore, setHasMore] = useState(true);
  const [isRefreshing, setIsRefreshing] = useState(false);
  const [isLoadingMore, setIsLoadingMore] = useState(false);
  
  // 新增:状态管理已查看的用户ID(用于反向传参接收)
  const [viewedUserIds, setViewedUserIds] = useState([]);

  // 原有请求逻辑不变
  const fetchUserList = async (currentPage = 1, isRefresh = false) => {
    try {
      if (isRefresh) {
        setIsRefreshing(true);
        setPage(1);
        setError('');
        setHasMore(true);
      } else {
        setIsLoadingMore(true);
      }

      const res = await service.get(`/users?_page=${currentPage}&_limit=${limit}`);
      if (res.length < limit) setHasMore(false);
      setUserList(prev => isRefresh ? res : [...prev, ...res]);
      setLoading(false);
    } catch (err) {
      setError(err.message);
      setLoading(false);
    } finally {
      setIsRefreshing(false);
      setIsLoadingMore(false);
    }
  };

  useEffect(() => {
    fetchUserList(1);
  }, []);

  // 新增:监听反向传递的参数(核心)
  useEffect(() => {
    const { isViewed, viewedUserId } = route.params ?? {};
    if (isViewed && viewedUserId) {
      // 避免重复添加已查看ID
      setViewedUserIds(prev => [...new Set([...prev, viewedUserId])]);
    }
  }, [route.params]);

  // 修改列表项渲染:添加点击事件+标记已查看
  const renderUserItem = ({ item }) => (
    <TouchableOpacity 
      style={styles.itemCard} 
      activeOpacity={0.9}
      // 新增:点击跳转详情页,正向传递参数
      onPress={() => {
        navigation.navigate('Detail', {
          userId: item.id,
          userName: item.name
        });
      }}
    >
      {/* 新增:已查看的项添加删除线样式 */}
      <Text style={[
        styles.itemName,
        viewedUserIds.includes(item.id) && { 
          textDecorationLine: 'line-through', 
          color: '#999' 
        },
        isBoard && { fontSize: 18 }
      ]}>
        {item.name} 
        {/* 新增:已查看标记 */}
        {viewedUserIds.includes(item.id) && ' ✔️'}
      </Text>
      <Text style={[styles.itemEmail, isBoard && { fontSize: 15 }]}>📧 {item.email}</Text>
      <Text style={[styles.itemPhone, isBoard && { fontSize: 15 }]}>📱 {item.phone}</Text>
    </TouchableOpacity>
  );

  // 原有底部加载提示不变
  const renderFooter = () => {
    if (isLoadingMore) return <Text style={styles.footerText}>加载更多中...</Text>;
    if (!hasMore && userList.length > 0) return <Text style={styles.footerText}>已加载全部数据✅</Text>;
    if (error && userList.length > 0) return (
      <TouchableOpacity onPress={() => fetchUserList(page)} style={styles.footerError}>
        <Text style={styles.footerErrorText}>加载失败,点击重试🔄</Text>
      </TouchableOpacity>
    );
    return <View style={styles.footerEmpty} />;
  };

  // 原有页面渲染逻辑不变
  if (loading) return <Text style={styles.loadingText}>首页加载中...</Text>;
  if (error) return (
    <TouchableOpacity onPress={() => fetchUserList(1)} style={styles.errorContainer}>
      <Text style={styles.errorText}>{error}</Text>
      <Text style={styles.retryText}>点击重试</Text>
    </TouchableOpacity>
  );

  return (
    <View style={styles.container}>
      <Text style={styles.pageTitle}>首页 📋</Text>
      <FlatList
        data={userList}
        keyExtractor={item => item.id.toString()}
        renderItem={renderUserItem}
        showsVerticalScrollIndicator={false}
        contentContainerStyle={styles.listContent}
        refreshControl={
          <RefreshControl
            refreshing={isRefreshing}
            onRefresh={() => fetchUserList(1, true)}
            tintColor="#007AFF"
            title="下拉刷新中..."
          />
        }
        onEndReached={() => {
          if (!hasMore || isLoadingMore || isRefreshing) return;
          fetchUserList(page + 1);
          setPage(prev => prev + 1);
        }}
        onEndReachedThreshold={0.1}
        ListFooterComponent={renderFooter}
      />
    </View>
  );
};

// 原有样式不变(可根据需要调整开发板适配)
const styles = StyleSheet.create({
  container: { flex: 1, backgroundColor: '#fff', paddingHorizontal: isBoard ? 20 : 15 },
  pageTitle: { fontSize: isBoard ? 22 : 20, textAlign: 'center', padding: 10, fontWeight: '600' },
  loadingText: { flex: 1, textAlign: 'center', textAlignVertical: 'center', fontSize: isBoard ? 18 : 16, color: '#666' },
  errorContainer: { flex: 1, justifyContent: 'center', alignItems: 'center' },
  errorText: { fontSize: isBoard ? 18 : 16, color: '#ff4444', marginBottom: 10 },
  retryText: { fontSize: isBoard ? 16 : 14, color: '#007AFF', textDecorationLine: 'underline' },
  listContent: { paddingVertical: 10 },
  itemCard: { padding: isBoard ? 20 : 15, borderBottomWidth: 1, borderBottomColor: '#f0f0f0', marginBottom: 10, borderRadius: 8 },
  itemName: { fontSize: isBoard ? 18 : 17, fontWeight: '600' },
  itemEmail: { fontSize: isBoard ? 15 : 14, color: '#666', marginTop: 5 },
  itemPhone: { fontSize: isBoard ? 15 : 14, color: '#888', marginTop: 3 },
  footerText: { padding: 15, textAlign: 'center', fontSize: isBoard ? 16 : 14, color: '#666' },
  footerError: { padding: 15, textAlign: 'center' },
  footerErrorText: { fontSize: isBoard ? 16 : 14, color: '#ff4444' },
  footerEmpty: { height: 10 },
});

export default Home;
2.1 验证跳转与正向传参
# 重启Metro服务(清除缓存)
npx react-native start --reset-cache

# 运行工程
react-native run-ohos --emulator

✅ 验证标准:

  1. 首页列表项可点击,点击后跳转到详情页;
  2. 详情页能正确显示对应用户信息,无参数缺失报错;
  3. 开发板/真机均能正常跳转,无卡顿。
2.2 Git提交
git add .
git commit -m "feat: 实现列表项点击跳转,完成正向参数传递"

3.3 步骤3:验证反向参数传递(10分钟)📥

3.1 功能验证
  1. 点击首页列表项→进入详情页;
  2. 点击详情页“✅ 标记为已查看”按钮→返回首页;
  3. 首页对应列表项显示删除线+✔️标记,确认反向传参生效。
3.2 多终端验证
终端类型 验证要点
💻 模拟器 跳转流畅,反向传参标记正常
📱 鸿蒙真机 兼容右滑返回,标记状态不丢失
🖥️ DAYU200开发板 按钮点击响应正常,标记显示清晰

3.4 步骤4:Git规范提交代码(10分钟)📜

# 添加所有修改
git add .

# 规范提交
git commit -m "feat: 开发详情页,完成正向/反向参数传递,适配多终端跳转"

# 推送至远程分支
git push origin feature-page-navigation

✅ 验证:打开代码仓库,确认feature-page-navigation分支提交记录正常,拉取代码后可正常运行。


❌ 四、常见问题与解决方案(10分钟,新手必看)

🚫 问题1:列表项点击无跳转,控制台提示「navigation.navigate is not a function」

✅ 解决方案:

  1. 确认Home组件已接收navigation参数:const Home = ({ navigation }) => {}
  2. 检查App.js路由配置,确保首页使用HomeStack(堆栈导航)而非直接使用Home组件;
  3. 确认NavigationContainer已包裹所有导航组件。

🚫 问题2:详情页无法接收参数,route.params为undefined

✅ 解决方案:

  1. 跳转时确认参数格式正确:
    // ✅ 正确
    navigation.navigate('Detail', { userId: item.id, userName: item.name });
    // ❌ 错误(缺少第二个参数)
    navigation.navigate('Detail');
    
  2. 接收参数时做容错处理:const { userId } = route.params ?? {}
  3. 检查Stack.Screen的name与跳转时一致(大小写敏感,如“Detail”≠“detail”)。

🚫 问题3:反向传递参数,首页无法接收

✅ 解决方案:

  1. 详情页跳转时使用首页堆栈的name(如“HomePage”而非“Home”):
    navigation.navigate('HomePage', { isViewed: true, viewedUserId: userId });
    
  2. 首页使用useEffect监听route.params变化(依赖项必须包含route.params);
  3. 右滑返回场景适配:使用navigation.setParams提前存储参数:
    // 详情页修改
    onPress={() => {
      navigation.setParams({ isViewed: true, viewedUserId: userId });
      navigation.goBack();
    }}
    

🚫 问题4:开发板跳转卡顿、详情页渲染缓慢

✅ 解决方案:

  1. 关闭跳转动画:Stack.Navigator screenOptions={{ animationEnabled: false }}
  2. 简化详情页样式,移除阴影/渐变等复杂样式;
  3. 优化网络请求:添加超时时间,减少请求数据量:
    // src/api/request.js 新增超时配置
    axios.defaults.timeout = 10000; // 10秒超时
    

🚫 问题5:真机右滑返回时,反向传递参数丢失

✅ 解决方案:

  1. 监听详情页的beforeRemove事件,右滑返回时自动传递参数:
    // Detail.js 新增
    useEffect(() => {
      const unsubscribe = navigation.addListener('beforeRemove', (e) => {
        // 右滑返回时传递默认参数
        navigation.navigate('HomePage', { isViewed: false });
      });
      return unsubscribe;
    }, [navigation]);
    

📝 五、课堂小结(5分钟)

本课时核心完成了页面跳转、参数传递与详情页开发,衔接第5课时的Tab导航内容,重点掌握4个核心要点:

  1. 🔑 页面跳转的核心是「路由配置+导航方法」,优先使用@react-navigation三方库,适配鸿蒙多终端,避免手动处理手势/状态差异🔄;
  2. 🔑 参数传递分为正向(列表→详情)和反向(详情→首页),正向传参携带跳转数据,反向传参同步操作结果,接收时需做好容错处理📤📥;
  3. 🔑 详情页开发的关键是「数据请求+样式适配+兜底处理」,依托正向参数请求数据,适配不同终端屏幕,避免白屏/报错🎨;
  4. 🔑 鸿蒙多终端适配的核心是「简化动画+适配触控方式」,开发板添加返回按钮,真机兼容右滑返回,确保跳转流畅、参数稳定🖥️。

本节课实现了应用的完整页面交互(列表→详情→返回同步),下一节课我们将进入新的核心模块——表单开发,实现用户输入、表单验证功能,进一步完善应用的实用性💪。


✅ 六、课后任务(必做)

📌 任务1:复盘核心功能🔄

独立完成「列表跳转详情+正向传参+反向传参+详情页渲染」全流程,熟练掌握路由配置和参数传递逻辑。

📌 任务2:优化跳转与详情页体验🎨

  1. 为跳转添加简单过渡动画(鸿蒙适配版):
    // Stack.Navigator配置
    screenOptions={{
      animation: 'slide_from_right', // 简单滑动动画
      animationEnabled: !isBoard
    }}
    
  2. 完善详情页样式:平板端分两列展示详情信息,开发板增大字体/间距;
  3. 优化反向传参逻辑:添加“取消已查看”按钮,支持移除标记。

📌 任务3:新增功能📄

  1. 在详情页添加“🏠 返回首页”按钮,实现直接返回首页(navigation.popToTop);
  2. 传递额外参数(如返回时间戳),首页接收并显示。

📌 任务4:多终端测试🚀

确保模拟器、真机、开发板上跳转流畅、参数传递稳定,记录适配调整点。

📌 任务5:预习📚

预习RN表单开发相关知识:

  • TextInput组件基础用法;
  • 表单状态管理(useState);
  • 表单验证逻辑(非空、格式校验)。

🎯 核心要点总结

  1. 🌟 路由配置:Tab导航嵌套Stack导航实现“Tab切换+页面跳转”,开发板关闭动画提升性能📦;
  2. 🌟 参数传递:正向传参用navigation.navigate,反向传参用route.params+useEffect监听,做好容错处理📤📥;
  3. 🌟 详情页适配:通过Dimensions判断终端类型,开发板增大字体/点击区域,避免内容溢出🎨;
  4. 🌟 鸿蒙兼容:开发板添加明确返回按钮,真机兼容右滑返回,简化动画减少卡顿🚀。
    在这里插入图片描述

若你在实操过程中遇到路由配置、参数传递、详情页渲染相关的问题,欢迎在评论区留言💬!下一节课,我们一起解锁表单开发,实现用户输入与验证,让应用功能更完善!🚀

关注我,后续课时持续更新,从0到1掌握RN兼容鸿蒙跨平台开发,夯实每一个核心页面交互环节🌟!

欢迎加入开源鸿蒙跨平台社区,https://openharmonycrossplatform.csdn.net

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐