uni-app中如何跳转?

一、 原生跳转方式 vs uni-app 统一映射

先看全景对照表,建立直觉:

原生概念 iOS Android 鸿蒙 uni-app 对应 API 说明
普通跳转(入栈) pushViewController startActivity / Fragment push router.pushUrl uni.navigateTo() 保留当前页,可返回
重定向(替换) setViewControllers startActivity + FLAG_CLEAR_TOP router.replaceUrl uni.redirectTo() 关闭当前页,打开新页
Tab 切换 UITabBarController BottomNavigationView + Fragment Tabs uni.switchTab() 只能跳 tabBar 页面
重启到首页 popToRootViewController FLAG_ACTIVITY_CLEAR_TASK router.clear() uni.reLaunch() 清空所有页面栈
返回上一页 popViewController finish() / onBackPressed router.back() uni.navigateBack() 支持 delta 回退多层
模态弹出 presentViewController DialogFragment / BottomSheet CustomDialog ❌ 无直接对应 用组件模拟(见下文)
预加载 prefetchViewController Preloading preloader uni.preloadPage() App/H5 端支持

⚠️ 核心认知: uni-app 的路由是基于页面栈 的,最大深度为 10 层。这与小程序的限制一致,App 端也遵守此约束。


二、 uni-app 五大路由 API 详解

2.1 uni.navigateTo() --- 最常用

javascript 复制代码
uni.navigateTo({
  url: '/pages/detail/detail?id=123&name=test',
  animationType: 'slide-in-right',   // ← 跳转动画(App端生效)
  animationDuration: 300,            // ← 动画时长 ms
  events: {                          // ← 页面间通信(监听目标页 emit)
    onDetailLoaded: (data) => {
      console.log('详情页加载完成', data);
    }
  },
  success: (res) => {
    // res.eventChannel 可用于向目标页传数据
    res.eventChannel.emit('initData', { id: 123 });
  }
});

原生对照:

  • iOS: pushViewController:animated:
  • Android: startActivity + slide 动画
  • 鸿蒙: router.pushUrl({ url }, RouterMode.Standard)

2.2 uni.redirectTo() --- 替换当前页

css 复制代码
uni.redirectTo({
  url: '/pages/login/login'
});

💡 使用场景: 登录成功后跳转到主页、表单提交成功后跳转到结果页。当前页不会保留在栈中,用户点返回不会回到这个页面。

2.3 uni.switchTab() --- TabBar 专属

arduino 复制代码
uni.switchTab({
  url: '/pages/index/index'  // ← 注意:不能带参数!
});

⚠️ 限制: URL 不能携带查询参数。如需传参,使用全局状态(Pinia/Vuex)、eventChanneluni.$emit/uni.$on

2.4 uni.reLaunch() --- 清空栈重启

css 复制代码
uni.reLaunch({
  url: '/pages/index/index'
});

💡 使用场景: 退出登录、Token 过期强制重新登录、支付完成后回到首页。

2.5 uni.navigateBack() --- 返回

php 复制代码
// 返回上一级
uni.navigateBack();

// 返回上三级
uni.navigateBack({ delta: 3 });

// 带自定义动画(App端)
uni.navigateBack({
  animationType: 'slide-out-right',
  animationDuration: 300
});

三、 不同设备上的跳转差异与适配

这是你最关心的部分。虽然 API 统一了,但各端的实际表现确实不同

3.1 跳转动画差异

平台 navigateTo 默认动画 navigateBack 默认动画 可自定义
iOS 右侧滑入 右侧滑出 animationType
Android 底部弹起/淡入 淡出 animationType
鸿蒙 右侧滑入 右侧滑出 animationType
微信小程序 右侧滑入 右侧滑出 ❌ 系统固定
H5 无动画 无动画 ⚠️ 需自行实现 CSS 过渡

App 端可用的 animationType:

arduino 复制代码
// 进入动画
'slide-in-right'   // 从右滑入(最常用)
'slide-in-left'    // 从左滑入
'slide-in-top'     // 从上滑入
'slide-in-bottom'  // 从下滑入
'fade-in'          // 淡入
'zoom-in'          // 缩放进入
'none'             // 无动画

// 退出动画(navigateBack 时使用)
'slide-out-right'  // 向右滑出
'slide-out-left'   // 向左滑出
'slide-out-top'    // 向上滑出
'slide-out-bottom' // 向下滑出
'fade-out'         // 淡出
'zoom-out'         // 缩放退出

3.2 页面间传参方式的跨端差异

传参方式 App H5 微信小程序 推荐度
URL Query (?id=1) ✅ 有长度限制 ⭐⭐⭐ 简单数据
eventChannel ⭐⭐⭐⭐⭐ 复杂对象
Pinia/Vuex ⭐⭐⭐⭐ 全局状态
uni. emit/emit/ emit/on ⭐⭐⭐ 事件通知
globalData ⚠️ ⭐⭐ 不推荐

eventChannel 最佳实践(推荐):

php 复制代码
// 源页面:跳转时传复杂对象
uni.navigateTo({
  url: '/pages/detail/detail',
  success: (res) => {
    // 向目标页发送数据
    res.eventChannel.emit('passData', {
      product: { id: 1, name: 'iPhone', specs: [...] },
      fromPage: 'home'
    });
  }
});

// 目标页:接收数据
onLoad(() => {
  const eventChannel = getOpenerEventChannel();
  eventChannel.on('passData', (data) => {
    console.log('收到数据:', data.product);
  });
  
  // 也可以向源页面回传数据
  eventChannel.emit('onDetailLoaded', { loaded: true });
});

💡 为什么推荐 eventChannel? URL 传参有长度限制(小程序约 2KB),且只能传字符串。eventChannel 可以传任意 JS 对象,且双向通信。

3.3 小程序端的特殊限制

限制项 说明 解决方案
页面栈上限 10 层 超过后 navigateTo 静默失败 合理使用 redirectTo/reLaunch
switchTab 不能带参 设计如此 用 Pinia 或 eventChannel
无法自定义转场动画 微信系统控制 接受或改用自定义组件模拟
返回按钮由系统渲染 左上角胶囊不可自定义 使用自定义导航栏组件
不支持 onPageScroll 节流 频繁触发 使用 IntersectionObserver 替代

四、 原生 Modal/BottomSheet 怎么办?

uni-app 没有 uni.presentModal() 这样的 API。因为小程序没有真正的模态页面概念。需要用以下方式模拟:

方案对比

方案 适用场景 优点 缺点
uni-popup 插件 弹窗/底部抽屉 开箱即用,跨端一致 不是真正的新页面
自定义 overlay 组件 高度定制的底部面板 完全可控 需要自己写动画和遮罩
navigateto + 透明背景 需要独立生命周期的"伪模态" 有独立页面栈 小程序不支持透明背景页
原生插件 必须用原生 UI 的场景 性能最好 失去跨端能力

推荐:使用 uni-popup 模拟 BottomSheet

xml 复制代码
<template>
  <view @tap="openSheet">打开底部面板</view>
  
  <uni-popup ref="popupRef" type="bottom" :safe-area="true">
    <view class="bottom-sheet">
      <view class="sheet-header">选择操作</view>
      <view class="sheet-item" @tap="onEdit">编辑</view>
      <view class="sheet-item" @tap="onDelete">删除</view>
      <view class="sheet-cancel" @tap="closeSheet">取消</view>
    </view>
  </uni-popup>
</template>

<script setup>
const popupRef = ref(null);
const openSheet = () => popupRef.value.open();
const closeSheet = () => popupRef.value.close();
</script>

五、 路由拦截与守卫(类似原生 AppDelegate/RouterInterceptor)

uni-app 没有 Vue Router 那样的全局守卫,但可以通过以下方式实现:

5.1 封装路由工具(推荐)

typescript 复制代码
// utils/router.ts
const WHITE_LIST = ['/pages/login/login', '/pages/index/index'];

export const navigateTo = (url: string, options?: any) => {
  const token = uni.getStorageSync('token');
  
  // 未登录且不在白名单 → 拦截跳转登录页
  if (!token && !WHITE_LIST.some(path => url.startsWith(path))) {
    uni.navigateTo({ url: `/pages/login/login?redirect=${encodeURIComponent(url)}` });
    return;
  }
  
  uni.navigateTo({ url, ...options });
};

// 使用时
import { navigateTo } from '@/utils/router';
navigateTo('/pages/order/confirm');  // 自动鉴权

5.2 App 端拦截器

javascript 复制代码
// main.ts 中(仅 App 端生效)
// #ifdef APP-PLUS
uni.addInterceptor('navigateTo', {
  invoke(args) {
    console.log('即将跳转:', args.url);
    // 可在此修改参数或阻止跳转
  },
  complete(res) {
    console.log('跳转完成:', res);
  }
});
// #endif

六、 给原生工程师的路由心智模型

perl 复制代码
┌─────────────────────────────────────────────────┐
│              uni-app 路由抽象层                    │
│  navigateTo / redirectTo / switchTab / reLaunch  │
├──────────┬──────────┬───────────┬───────────────┤
│   iOS    │ Android  │   鸿蒙    │   小程序/H5    │
│ NavCtrl  │ Activity │  Router   │  Page Stack    │
│ push/pop │ start/   │ pushUrl/  │  navigateTo/   │
│ present  │ finish   │ back      │  switchTab     │
│ modal→   │ Dialog→  │ Dialog→   │  overlay 组件   │
│ 组件模拟  │ 组件模拟  │ 组件模拟   │  (非真页面)    │
└──────────┴──────────┴───────────┴───────────────┘

💡 一句话总结: uni-app 的路由是 "小程序页面栈模型 + App 原生转场动画 + 组件模拟模态" 的混合体。它牺牲了部分原生跳转的灵活性(如真正的 modal page、自定义转场手势),换来了跨端一致性。对于绝大多数业务场景,这套抽象足够用;对于极端原生体验需求,通过条件编译 + 原生插件兜底。

需要我继续深入讲分包路由(subPackages)或者路由传参的性能优化吗?这两个话题在大项目中非常关键。

相关推荐
光影少年1 小时前
Codex 斜杠指令体系与业务场景调度实战
前端·react.js·reactivex
Y3815326622 小时前
SERP API 请求体 Gzip 压缩优化实战:大数据量降带宽 80%
开发语言·前端·php
踩着两条虫3 小时前
VTJ.PRO AI Agent 技术白皮书
前端·vue.js·低代码
子兮曰3 小时前
解剖 Claude Code:从入口架构逆向工程看 AI 编程工具的信任边界设计
前端·后端·claude
先吃饱再说3 小时前
层层传递太“痛”了:useContext 给 React 组件装了个“无线遥控器”
前端·react.js·前端框架
颜进强3 小时前
前端看后端 15:什么是 DNS?
前端·后端·ai编程
文艺理科生3 小时前
LangChain.js-v1-记忆管理最佳实践
前端·javascript·后端
子兮曰3 小时前
Elysia vs Hono 完整性能对比:Bun 专属优化还是跨平台通用,2026 实测数据给出的答案
前端·后端·typescript
To_OC4 小时前
从一个颜色选择器说起:我终于整明白了 React+TS 里的 model 与 api 分层
前端·react.js·typescript