uniapp的navigator跳转功能
接下来,我将围绕一个常见的电商小程序来构建一系列连贯的使用场景。在这个过程中,我们将把 <navigator>
组件的所有关键属性和方法都串联起来,并详细解释它们在每个环节所扮演的角色和作用。
核心场景:构建一个电商小程序的用户购物流程
想象一下,我们正在开发一个名为“优品汇”的电商小程序。用户可以浏览商品、查看详情、加入购物车、下单支付、以及跳转到合作的客服小程序。这个流程几乎涵盖了 <navigator>
的所有核心功能。
准备工作:项目结构与 pages.json
请在你的 uni-app
项目中,确保有如下的目录结构,并将 pages.json
文件配置如下。这为我们所有的导航场景提供了基础。
目录结构 (部分):
├── pages
│ ├── index
│ │ └── index.vue
│ ├── product
│ │ ├── list.vue
│ │ └── detail.vue
│ ├── order
│ │ ├── list.vue
│ │ └── detail.vue
│ ├── payment
│ │ └── success.vue
│ └── tabBar
│ ├── cart
│ │ └── cart.vue
│ └── my
│ └── my.vue
├── static
│ └── ...
└── pages.json
pages.json
配置:
{"pages": [{"path": "pages/index/index","style": { "navigationBarTitleText": "优品汇首页" }},{"path": "pages/product/list","style": { "navigationBarTitleText": "商品列表" }},{"path": "pages/product/detail","style": { "navigationBarTitleText": "商品详情" }},{"path": "pages/order/list","style": { "navigationBarTitleText": "我的订单" }},{"path": "pages/order/detail","style": { "navigationBarTitleText": "订单详情" }},{"path": "pages/payment/success","style": { "navigationBarTitleText": "支付成功" }},{"path": "pages/tabBar/cart/cart","style": { "navigationBarTitleText": "购物车" }},{"path": "pages/tabBar/my/my","style": { "navigationBarTitleText": "我的" }}],"globalStyle": {"navigationBarTextStyle": "black","navigationBarTitleText": "优品汇","navigationBarBackgroundColor": "#F8F8F8","backgroundColor": "#F8F8F8"},"tabBar": {"list": [{"pagePath": "pages/index/index","text": "首页"},{"pagePath": "pages/tabBar/cart/cart","text": "购物车"},{"pagePath": "pages/tabBar/my/my","text": "我的"}]}
}
场景一:从首页进入商品列表页(基础跳转与交互定制)
用户故事: 用户点击首页“限时抢购”入口,平滑跳转到商品列表页,并能看到清晰的点击反馈。
1. 首页 pages/index/index.vue
(源页面)
<template><view class="content"><view class="promo-section"><!-- 这是一个典型的页面跳转。我们在这里定制了用户交互的每一个细节。--><navigatorurl="/pages/product/list?activityId=promo123&from=home"open-type="navigate"hover-class="promo-hover":hover-start-time="50":hover-stay-time="200"><view class="promo-card"><text class="promo-title">限时抢购</text><text class="promo-desc">点击查看详情</text></view></navigator></view></view>
</template><script>
export default {data() {return {};}
};
</script><style>
.content {padding: 40rpx;
}
.promo-card {background-color: #e54d42;color: white;padding: 60rpx 40rpx;border-radius: 16rpx;text-align: center;box-shadow: 0 4px 10px rgba(229, 77, 66, 0.4);
}
.promo-title {font-size: 48rpx;font-weight: bold;
}
.promo-desc {font-size: 28rpx;margin-top: 10rpx;opacity: 0.9;
}
/* 自定义点击效果:轻微下沉和变暗 */
.promo-hover {opacity: 0.9;transform: scale(0.98);transition: all 0.2s;
}
</style>
2. 商品列表页 pages/product/list.vue
(目标页面)
<template><view class="container"><view v-if="activityId" class="activity-banner"><text>当前活动ID:{{ activityId }}</text><text>来源页面:{{ fromPage }}</text></view><view class="product-list"><text>这里是商品列表...</text></view></view>
</template><script>
export default {data() {return {activityId: '',fromPage: ''};},// onLoad生命周期函数用于接收页面参数onLoad(options) {console.log('接收到的页面参数:', options);if (options.activityId) {this.activityId = options.activityId;}if (options.from) {this.fromPage = options.from;}}
};
</script><style>
.container {padding: 20rpx;
}
.activity-banner {background-color: #fffbe6;border: 1px solid #ffe58f;padding: 20rpx;border-radius: 8rpx;margin-bottom: 20rpx;display: flex;flex-direction: column;
}
</style>
关键属性解析:
-
url: "/pages/product/list?activityId=promo123"
- 作用: 定义了导航的目标路径。这里我们使用了相对路径,并附带了一个查询参数
activityId
,这样商品列表页(list.vue
)就能通过onLoad
生命周期函数获取到这个参数,从而展示特定活动下的商品。这是页面间通信最常用、最基础的方式。
- 作用: 定义了导航的目标路径。这里我们使用了相对路径,并附带了一个查询参数
-
open-type: "navigate"
- 作用: 这是最标准的跳转方式,它会“保留”当前页面(首页),然后“推入”一个新的页面(商品列表页)到页面栈中。用户可以从商品列表页通过左上角的返回按钮回到首页。这符合用户“进入-返回”的心智模型。
-
hover-class: "promo-hover"
- 作用: 极大地提升了用户体验。当用户的指尖按下这个区域时,会立即应用
.promo-hover
这个 CSS 类,提供一个即时的视觉反馈(在这里是透明度和缩放效果)。这告诉用户:“系统已经响应了你的操作”。
- 作用: 极大地提升了用户体验。当用户的指尖按下这个区域时,会立即应用
-
hover-start-time: 50
- 作用: 定义了从用户按下到
hover-class
生效的延迟时间(50毫秒)。这个值可以防止用户在快速划过屏幕时不小心触发点击效果,让交互更精准。
- 作用: 定义了从用户按下到
-
hover-stay-time: 200
- 作用: 定义了用户手指松开后,点击效果还会“停留”的时间(200毫秒)。这给用户一种流畅、不生硬的感觉,尤其是在性能稍差的设备上,可以确保用户能看到完整的点击动画。
代码解析与运行指南:
- 作用: 定义了用户手指松开后,点击效果还会“停留”的时间(200毫秒)。这给用户一种流畅、不生硬的感觉,尤其是在性能稍差的设备上,可以确保用户能看到完整的点击动画。
- 运行项目,在首页点击“限时抢购”卡片。
- 你会观察到卡片有轻微下沉和变暗的动画效果,这是
hover-class
、hover-start-time
和hover-stay-time
共同作用的结果。 - 页面会跳转到“商品列表”,并且页面顶部的黄色提示框会显示出我们从
url
中传递过来的参数activityId
和from
。这验证了onLoad
函数成功接收了参数。 - 你可以点击原生导航栏的返回按钮,会返回到首页,这是
open-type="navigate"
的效果。
场景二:从商品列表跳转到 TabBar 购物车页
用户故事: 在商品列表页,用户点击悬浮的购物车图标,直接切换到底部导航的购物车页面。
1. 商品列表页 pages/product/list.vue
(在场景一的基础上增加)
<template><view class="container"><!-- ... (场景一中的代码) ... --><view class="product-list"><text>这里是商品列表...</text><!-- 在这里添加一个跳转到商品详情的链接,为场景五做准备 --><navigatorurl="/pages/product/detail?id=P001"open-type="navigate"class="product-item-link"animation-type="fade-in":animation-duration="400"><button type="default">查看商品P001详情(App有渐变动画)</button></navigator></view><!-- 购物车悬浮按钮 --><navigatorurl="/pages/tabBar/cart/cart"open-type="switchTab"hover-class="none"><view class="fab-cart-btn"><text>🛒</text></view></navigator></view>
</template><script>
// ... (与场景一相同) ...
export default {data() {return {activityId: '',fromPage: ''};},onLoad(options) {console.log('接收到的页面参数:', options);if (options.activityId) {this.activityId = options.activityId;}if (options.from) {this.fromPage = options.from;}}
};
</script><style>
/* ... (场景一的样式) ... */
.container {padding: 20rpx;
}
.activity-banner {background-color: #fffbe6;border: 1px solid #ffe58f;padding: 20rpx;border-radius: 8rpx;margin-bottom: 20rpx;display: flex;flex-direction: column;
}
.product-item-link {margin-top: 40rpx;
}
.fab-cart-btn {position: fixed;bottom: 100rpx;right: 40rpx;width: 100rpx;height: 100rpx;background-color: #007aff;border-radius: 50%;display: flex;justify-content: center;align-items: center;font-size: 50rpx;color: white;box-shadow: 0 4px 12px rgba(0, 122, 255, 0.4);
}
</style>
2. 购物车页 pages/tabBar/cart/cart.vue
(目标页面)
<template><view><view class="title">购物车</view><text>这里是您的购物车页面。</text></view>
</template>
代码解析与运行指南:
- 从首页进入商品列表页。
- 点击右下角的蓝色圆形购物车按钮。
- 应用会立即切换到“购物车”Tab,并且底部的 TabBar 会高亮“购物车”项。
- 核心观察点: 此时,你无法通过返回手势或返回按钮回到“商品列表页”。这是因为
open-type="switchTab"
清空了所有非 TabBar 的页面栈,确保了导航的扁平化。 hover-class="none"
确保了点击这个图标按钮时,它本身不会有任何多余的视觉变化。
场景三:下单成功后,使用 redirect
和 reLaunch
用户故事: 用户支付成功后,可以选择“查看订单”(不希望再返回支付页)或“返回首页”(开始一次全新的会话)。
1. 支付成功页 pages/payment/success.vue
<template><view class="container"><view class="icon-success">✔</view><text class="status-text">支付成功!</text><view class="button-area"><!-- 使用 reLaunch 返回首页。这会清空所有页面栈,只留下首页。--><navigator url="/pages/index/index" open-type="reLaunch"><button class="plain-btn">返回首页</button></navigator><!-- 使用 redirect 跳转到订单页。这会关闭当前页面,用订单列表页替换它。--><navigator url="/pages/order/list" open-type="redirect"><button type="primary">查看订单</button></navigator></view></view>
</template><script>
export default {data() {return {};}
};
</script><style>
.container {display: flex;flex-direction: column;align-items: center;padding-top: 150rpx;
}
.icon-success {font-size: 120rpx;color: #09bb07;border: 5px solid #09bb07;border-radius: 50%;width: 180rpx;height: 180rpx;display: flex;justify-content: center;align-items: center;
}
.status-text {font-size: 40rpx;margin-top: 40rpx;margin-bottom: 80rpx;
}
.button-area {display: flex;width: 100%;justify-content: space-around;
}
.plain-btn {background-color: white;border: 1px solid #ccc;
}
</style>
2. 订单列表页 pages/order/list.vue
(目标页面)
<template><view><text>这是您的订单列表。</text></view>
</template>
代码解析与运行指南:
- 为了模拟,你可以先手动跳转到支付成功页(例如,在首页加一个临时的
<navigator url="/pages/payment/success">
)。 - 测试
redirect
: 点击“查看订单”按钮。页面会跳转到订单列表。此时,点击导航栏返回按钮,你会发现你回到了进入支付成功页之前的那个页面(比如首页),而不是支付成功页。这证明了redirect
成功地替换了页面栈中的当前页。 - 测试
reLaunch
: 重新进入支付成功页。这次点击“返回首页”按钮。页面会跳转到首页。此时,页面栈被完全清空,你无法返回到任何之前的页面,就像刚打开小程序一样。
场景四:navigateBack
, target
与 hover-stop-propagation
用户故事: 在订单详情页,用户可以返回多级页面、跳转到客服小程序,并与页面内复杂组件进行无干扰的交互。
1. 订单详情页 pages/order/detail.vue
为了模拟此场景,你需要手动按顺序打开页面:首页 -> 我的(Tab) -> [添加一个按钮跳转到订单列表] -> [订单列表页添加按钮跳转到订单详情]
。这样才能构造出足够深的页面栈。
<template><view class="container"><text class="title">订单详情</text><!-- 场景4.1: 返回多级页面 --><view class="card"><text>假设我们的页面路径是: 我的 -> 订单列表 -> 订单详情。</text><text>点击下方按钮将直接返回“我的”页面。</text><navigator open-type="navigateBack" :delta="2"><button type="default">返回“我的” (返回2级)</button></navigator></view><!-- 场景4.2: 跳转到外部小程序 --><view class="card"><text>点击下方按钮,将打开合作的客服小程序。</text><!-- 注意: app-id需要真实存在且在manifest.json中配置 --><navigatortarget="miniProgram"open-type="navigate"app-id="wxf8e0cf27a58a75e2" path="pages/index/index"><button type="primary">联系客服(跳转到“小程序示例”)</button></navigator></view><!-- 场景4.3: 阻止事件冒泡 --><view class="card"><text>下方卡片整体可点击,但内部按钮的点击效果不会影响卡片。</text><navigator url="/pages/product/detail?id=P002" class="product-card" hover-class="card-hover"><view class="product-info"><text>可点击的商品卡片</text><text>点击空白处或文字,整个卡片会有点击效果。</text></view><!-- @click.native 确保在navigator内button的点击事件能触发 --><button class="fav-button" :hover-stop-propagation="true" @click.native="onFavorite">收藏</button></navigator></view></view>
</template><script>
export default {methods: {onFavorite() {uni.showToast({title: '收藏成功!',icon: 'none'});// 这里会执行收藏逻辑,但不会触发父级navigator的hover效果}}
}
</script><style>
.container { padding: 20rpx; }
.card { margin-bottom: 30rpx; padding: 20rpx; border: 1px solid #eee; border-radius: 8rpx; }
.title { font-size: 36rpx; font-weight: bold; margin-bottom: 20rpx; }
button { margin-top: 20rpx; }.product-card {display: flex;justify-content: space-between;align-items: center;padding: 20rpx;background-color: #f9f9f9;border-radius: 8rpx;
}
.card-hover {background-color: #e0e0e0;
}
.fav-button {margin: 0; /* 重置按钮的默认margin */background-color: #ffc107;color: white;
}
</style>
代码解析与运行指南:
navigateBack
&delta
: 确保你有至少3层页面栈(如我的
->订单列表
->订单详情
)。在订单详情页点击“返回‘我的’”按钮,你会看到应用跳过了订单列表,直接回到了“我的”页面。target="miniProgram"
: 点击“联系客服”按钮。如果你的环境支持(如微信开发者工具或真机)且app-id
有效(这里用的是微信官方的“小程序示例”appid),它会拉起另一个小程序。hover-stop-propagation
:- 先点击“可点击的商品卡片”的文字或空白区域,整个卡片背景会变灰(
card-hover
效果)。 - 然后,只点击黄色的“收藏”按钮。你会看到按钮有自己的点击效果,并且弹出了“收藏成功!”的提示,但整个卡片的背景不会变灰。这证明了
hover-stop-propagation
成功阻止了点击态的冒泡。
- 先点击“可点击的商品卡片”的文字或空白区域,整个卡片背景会变灰(
场景五:为 App 端定制页面过渡动画
用户故事: 在 App 端,从商品列表点击某个商品时,希望商品详情页以“渐显”的方式出现,而不是平台默认的侧滑。
1. 商品列表页 pages/product/list.vue
(在场景二的代码中已包含)
我们复用场景二中的代码,重点关注那个跳转到详情页的 <navigator>
。
<!-- 在 pages/product/list.vue 中 -->
<navigatorurl="/pages/product/detail?id=P001"open-type="navigate"class="product-item-link"<!-- 以下两个属性是本场景核心 -->animation-type="fade-in":animation-duration="400"
><button type="default">查看商品P001详情(App有渐变动画)</button>
</navigator>
2. 商品详情页 pages/product/detail.vue
(目标页面)
<template><view><text>商品详情页</text><text v-if="productId">商品ID: {{ productId }}</text></view>
</template><script>export default {data() {return {productId: ''}},onLoad(options) {this.productId = options.id}}
</script>
代码解析与运行指南:
- 重要: 此效果仅在 App 端可见。请使用 HBuilderX 将项目运行到手机或模拟器上。在小程序开发者工具或 H5 端,这两个属性会被忽略。
- 在 App 中,从首页进入商品列表页。
- 点击“查看商品P001详情”按钮。
- 你会观察到,商品详情页不是从右侧滑入的,而是以一个持续 400 毫秒的渐显动画出现。返回时,它会以“渐隐”动画消失。
- 这证明了
animation-type
和animation-duration
成功地定制了 App 端的原生转场动画,提升了应用的独特性和品质感。