跳到主要内容
哒哒星球DADA PLANET

Skill · 已公开版本

expo-router · 用 Expo Router 组织应用页面与导航

把页面路径、参数、标签页和模态入口组织成清晰的文件路由,处理平台差异与返回行为。

先看看示例

作者提供的示例,实际效果取决于你的输入和使用工具。

输入

已有 Expo 项目保留根布局与 / 路由。新增 /orders 列表和 /orders/[id] 详情;演示只使用本地 demo-42 订单,未知 id 显示未找到;接入真实接口时另核验登录与对象权限。

输出

编辑示例 · 未运行

// src/app/orders/_layout.tsx
import { Stack } from "expo-router/stack";

export default function OrdersLayout() {
  return (
    <Stack>
      <Stack.Screen name="index" options={{ title: "订单" }} />
      <Stack.Screen name="[id]" options={{ title: "订单详情" }} />
    </Stack>
  );
}
// src/app/orders/index.tsx
import { Link } from "expo-router";
import { Text, View } from "react-native";

export default function OrdersScreen() {
  return (
    <View>
      <Text>订单列表</Text>
      <Link href={{ pathname: "/orders/[id]", params: { id: "demo-42" } }}>
        查看示例订单
      </Link>
    </View>
  );
}
// src/app/orders/[id].tsx
import { useEffect, useState } from "react";
import { useLocalSearchParams } from "expo-router";
import { ActivityIndicator, Text, View } from "react-native";

export default function OrderDetailScreen() {
  const { id } = useLocalSearchParams<{ id?: string | string[] }>();
  const orderId = typeof id === "string" ? id : null;
  const [loading, setLoading] = useState(true);
  const [order, setOrder] = useState<{ id: string; title: string } | null>(null);

  useEffect(() => {
    let active = true;
    setLoading(true);
    // 本地演示数据;真实接口还需错误处理和服务端权限检查。
    const record = orderId === "demo-42"
      ? { id: orderId, title: "示例订单" }
      : null;
    Promise.resolve(record).then(value => {
      if (active) {
        setOrder(value);
        setLoading(false);
      }
    });
    return () => { active = false; };
  }, [orderId]);

  if (loading) return <ActivityIndicator accessibilityLabel="正在加载订单" />;
  if (!order) return <Text>未找到订单</Text>;
  return <View><Text>{order.title}</Text><Text>编号:{order.id}</Text></View>;
}

三个代码块分别保存为注释标明的文件,加入现有根布局。Hook 位于详情组件内部,所用组件均显式导入;这是未运行的本地演示,真实数据请求还需独立处理加载失败和无权访问。

方法说明

把页面路径、参数、标签页和模态入口组织成清晰的文件路由,处理平台差异与返回行为。

具体任务

建立订单列表到详情的路由:把页面路径、参数、标签页和模态入口组织成清晰的文件路由,处理平台差异与返回行为。

组织弹窗和标签导航:按页面关系配置 Stack、路由组和 Modal,检查深链接进入与关闭后的去向。

必需材料

Expo 项目与 Router 版本

页面清单、参数与返回规则

可选材料

深链接示例

原生平台目标

使用门槛

了解 app 或 src/app 的文件路由约定,并可运行目标平台。

这个 Skill 额外提供的方法

用路由文件、布局和明确返回路径组织页面,避免参数和导航状态散落在组件里。

编辑示例(未运行)

建立订单列表到详情的路由

任务:请根据下面的示例材料完成建立订单列表到详情的路由,把判断依据和待确认事项写清楚。

输入:
已有 Expo 项目保留根布局与 / 路由。新增 /orders 列表和 /orders/[id] 详情;演示只使用本地 demo-42 订单,未知 id 显示未找到;接入真实接口时另核验登录与对象权限。

结果片段:
```tsx
// src/app/orders/_layout.tsx
import { Stack } from "expo-router/stack";

export default function OrdersLayout() {
  return (
    <Stack>
      <Stack.Screen name="index" options={{ title: "订单" }} />
      <Stack.Screen name="[id]" options={{ title: "订单详情" }} />
    </Stack>
  );
}
```

```tsx
// src/app/orders/index.tsx
import { Link } from "expo-router";
import { Text, View } from "react-native";

export default function OrdersScreen() {
  return (
    <View>
      <Text>订单列表</Text>
      <Link href={{ pathname: "/orders/[id]", params: { id: "demo-42" } }}>
        查看示例订单
      </Link>
    </View>
  );
}
```

```tsx
// src/app/orders/[id].tsx
import { useEffect, useState } from "react";
import { useLocalSearchParams } from "expo-router";
import { ActivityIndicator, Text, View } from "react-native";

export default function OrderDetailScreen() {
  const { id } = useLocalSearchParams<{ id?: string | string[] }>();
  const orderId = typeof id === "string" ? id : null;
  const [loading, setLoading] = useState(true);
  const [order, setOrder] = useState<{ id: string; title: string } | null>(null);

  useEffect(() => {
    let active = true;
    setLoading(true);
    // 本地演示数据;真实接口还需错误处理和服务端权限检查。
    const record = orderId === "demo-42"
      ? { id: orderId, title: "示例订单" }
      : null;
    Promise.resolve(record).then(value => {
      if (active) {
        setOrder(value);
        setLoading(false);
      }
    });
    return () => { active = false; };
  }, [orderId]);

  if (loading) return <ActivityIndicator accessibilityLabel="正在加载订单" />;
  if (!order) return <Text>未找到订单</Text>;
  return <View><Text>{order.title}</Text><Text>编号:{order.id}</Text></View>;
}
```

三个代码块分别保存为注释标明的文件,加入现有根布局。Hook 位于详情组件内部,所用组件均显式导入;这是未运行的本地演示,真实数据请求还需独立处理加载失败和无权访问。

用路由文件、布局和明确返回路径组织页面,避免参数和导航状态散落在组件里。 该片段是编辑示例,用于说明交付形态,未运行上游技能。

首次任务

请使用「用 Expo Router 组织应用页面与导航」完成「建立订单列表到详情的路由」。
任务要求:用路由文件、布局和明确返回路径组织页面,避免参数和导航状态散落在组件里。

我的必需材料:
- Expo 项目与 Router 版本:[请填写或粘贴]
- 页面清单、参数与返回规则:[请填写或粘贴]

请基于上述实际材料输出:路由结构、导航配置与返回路径验证。缺少依据的地方单列为待确认,不编造事实;未执行的操作或测试明确标为未执行。

验收要求:
1. 列表进入和返回保留合理上下文。
2. 直接打开详情链接可处理参数和缺失数据。
3. 目标平台导航行为与版本支持一致。

验收方法

列表进入和返回保留合理上下文

直接打开详情链接可处理参数和缺失数据

目标平台导航行为与版本支持一致

依赖与准备

需要兼容版本的 Expo、expo-router 和相应平台运行环境;保留技能引用的路由结构与导航参考,原生功能按 SDK 支持核对。

使用边界

原生 Tabs、菜单与 Sheet 能力受 Expo SDK 和平台版本影响。

导航代码不会代替服务端授权,详情页仍需检查数据访问权限。

排错

返回跳到意外页面:检查路由组和初始路由设置,分别验证站内进入与深链接进入。

参数有时是数组或空值:校验路由参数后再加载,不直接拼接请求。

固定来源

https://github.com/expo/skills/blob/c0dadf355d4caa4e1720de372f0f8766df1a8978/plugins/expo/skills/expo-router/SKILL.md