导航与路由(navigateTo、tabBar 等)
路由跳转的基本方法
uni-app提供了多种路由跳转方式,最基础的是uni.navigateTo
方法。这个方法会保留当前页面,跳转到应用内的某个页面,使用uni.navigateBack
可以返回到原页面。
// 跳转到指定页面
uni.navigateTo({
url: '/pages/detail/detail?id=1'
});
// 接收参数的页面
onLoad(options) {
console.log(options.id); // 输出1
}
路由跳转时可以传递参数,参数会自动拼接到url后面。目标页面在onLoad
生命周期中通过options
参数获取这些值。
特殊路由跳转方式
除了常规跳转外,uni-app还提供了几种特殊的路由方法:
uni.redirectTo
- 关闭当前页面,跳转到应用内的某个页面
uni.redirectTo({
url: '/pages/login/login'
});
uni.reLaunch
- 关闭所有页面,打开到应用内的某个页面
uni.reLaunch({
url: '/pages/index/index'
});
uni.switchTab
- 跳转到tabBar页面,并关闭其他所有非tabBar页面
uni.switchTab({
url: '/pages/home/home'
});
tabBar配置与使用
tabBar是uni-app中实现底部导航栏的功能。需要在pages.json
中进行配置:
{
"tabBar": {
"color": "#999",
"selectedColor": "#007AFF",
"backgroundColor": "#ffffff",
"borderStyle": "black",
"list": [
{
"pagePath": "pages/home/home",
"text": "首页",
"iconPath": "static/tab-home.png",
"selectedIconPath": "static/tab-home-active.png"
},
{
"pagePath": "pages/user/user",
"text": "我的",
"iconPath": "static/tab-user.png",
"selectedIconPath": "static/tab-user-active.png"
}
]
}
}
tabBar页面有一些特殊限制:
- 不能使用
navigateTo
跳转到tabBar页面,必须使用switchTab
- tabBar页面在切换时不会重新加载,会保持之前的状态
路由传参的多种方式
除了通过url传递简单参数外,还可以使用以下方式传递复杂数据:
- 全局变量方式
// 发送页面
uni.navigateTo({
url: '/pages/detail/detail',
success: function(res) {
res.eventChannel.emit('acceptData', {data: '复杂数据'});
}
});
// 接收页面
onLoad(options) {
const eventChannel = this.getOpenerEventChannel();
eventChannel.on('acceptData', function(data) {
console.log(data); // {data: '复杂数据'}
});
}
- Vuex状态管理
// store.js
state: {
routeParams: {}
},
mutations: {
setRouteParams(state, params) {
state.routeParams = params;
}
}
// 发送页面
this.$store.commit('setRouteParams', {id: 123});
uni.navigateTo({url: '/pages/detail/detail'});
// 接收页面
computed: {
params() {
return this.$store.state.routeParams;
}
}
路由拦截与权限控制
在实际项目中,经常需要对路由跳转进行拦截,实现登录验证等权限控制功能。可以通过重写路由方法实现:
// 封装路由跳转方法
const myNavigateTo = (options) => {
if (options.url.indexOf('/pages/member/') !== -1) {
if (!uni.getStorageSync('token')) {
uni.showToast({title: '请先登录', icon: 'none'});
uni.navigateTo({url: '/pages/login/login'});
return;
}
}
uni.navigateTo(options);
};
// 使用封装的方法
myNavigateTo({
url: '/pages/member/center'
});
路由动画与自定义效果
uni-app支持通过配置实现页面跳转动画效果:
uni.navigateTo({
url: '/pages/detail/detail',
animationType: 'slide-in-right',
animationDuration: 300
});
支持的动画类型包括:
slide-in-right
- 从右侧横向滑动进入slide-in-left
- 从左侧横向滑动进入slide-in-top
- 从上侧竖向滑动进入slide-in-bottom
- 从下侧竖向滑动进入fade-in
- 淡入zoom-out
- 放大进入zoom-fade-out
- 放大淡入pop-in
- 从右侧平移进入
路由API的进阶用法
- 获取当前路由栈信息
const pages = getCurrentPages();
const currentPage = pages[pages.length - 1];
console.log(currentPage.route); // 当前页面路径
- 动态修改页面标题
uni.setNavigationBarTitle({
title: '新标题'
});
- 监听路由变化
// 在App.vue中
onShow() {
const pages = getCurrentPages();
const currentPage = pages[pages.length - 1];
this.$bus.$emit('routeChange', currentPage.route);
}
常见问题与解决方案
- 页面返回刷新问题
// 前一个页面添加监听
onShow() {
if (this.$refs.list) {
this.$refs.list.refresh();
}
}
- 页面栈超过10层限制
// 使用redirectTo代替navigateTo
uni.redirectTo({
url: '/pages/newpage/newpage'
});
- tabBar红点提示
uni.showTabBarRedDot({
index: 1 // tabBar的索引
});
- 动态隐藏tabBar
uni.hideTabBar({
animation: true
});
跨平台路由差异处理
不同平台的路由行为可能有差异,需要进行兼容处理:
// 判断平台
const isH5 = process.env.VUE_APP_PLATFORM === 'h5';
// 平台特定处理
if (isH5) {
// H5特有的路由逻辑
window.addEventListener('popstate', this.handlePopState);
} else {
// 小程序/App的路由逻辑
uni.onAppRoute((res) => {
console.log(res.path);
});
}
路由性能优化技巧
- 预加载页面
uni.preloadPage({
url: '/pages/detail/detail'
});
- 分包加载
// pages.json
{
"subPackages": [
{
"root": "pages/sub",
"pages": [
{
"path": "moduleA",
"style": {}
}
]
}
]
}
- 路由懒加载
// 动态导入组件
components: {
'lazy-component': () => import('@/components/lazy-component.vue')
}
路由与页面生命周期的关系
理解路由跳转时页面生命周期的触发顺序很重要:
-
从A页面navigateTo到B页面:
- A页面:onHide
- B页面:onLoad → onShow → onReady
-
从B页面navigateBack到A页面:
- B页面:onUnload
- A页面:onShow
-
使用redirectTo时:
- 原页面:onUnload
- 新页面:onLoad → onShow → onReady
路由参数的安全处理
处理路由参数时需要注意安全性:
- 参数校验
onLoad(options) {
const id = parseInt(options.id);
if (isNaN(id) || id <= 0) {
uni.redirectTo({url: '/pages/error/error'});
return;
}
this.loadData(id);
}
- 参数编码解码
// 发送时编码
uni.navigateTo({
url: `/pages/detail/detail?title=${encodeURIComponent('包含特殊字符的参数&value=1')}`
});
// 接收时解码
onLoad(options) {
const title = decodeURIComponent(options.title);
}
路由与页面通信的高级模式
- 使用EventBus进行跨页面通信
// 发送页面
this.$bus.$emit('data-update', {newData: 'value'});
// 接收页面
created() {
this.$bus.$on('data-update', this.handleDataUpdate);
},
beforeDestroy() {
this.$bus.$off('data-update', this.handleDataUpdate);
}
- 使用Vuex进行状态共享
// 发送页面
this.$store.commit('updateData', payload);
// 接收页面
computed: {
sharedData() {
return this.$store.state.sharedData;
}
}
路由钩子的实现方式
虽然uni-app没有直接提供路由钩子,但可以通过以下方式模拟:
- 全局路由钩子
// main.js
const originalNavigateTo = uni.navigateTo;
uni.navigateTo = function(options) {
console.log('即将跳转到:', options.url);
originalNavigateTo.call(uni, options);
};
- 页面级路由钩子
// 在页面中
onShow() {
if (this.$options.routeHook && this.$options.routeHook.beforeEnter) {
this.$options.routeHook.beforeEnter.call(this);
}
}
路由与页面滚动位置
处理页面滚动位置是常见的需求:
- 保存滚动位置
onPageScroll(e) {
this.scrollTop = e.scrollTop;
},
onHide() {
uni.setStorageSync('pageScroll_' + this.$route.path, this.scrollTop);
}
- 恢复滚动位置
onShow() {
const savedPosition = uni.getStorageSync('pageScroll_' + this.$route.path);
if (savedPosition) {
uni.pageScrollTo({
scrollTop: savedPosition,
duration: 0
});
}
}
动态路由与权限控制
根据用户权限动态生成路由表:
// 根据权限过滤路由
function filterRoutes(routes, permissions) {
return routes.filter(route => {
if (route.meta && route.meta.permission) {
return permissions.includes(route.meta.permission);
}
return true;
});
}
// 动态修改pages.json
const dynamicRoutes = filterRoutes(allRoutes, userPermissions);
uni.setTabBarItems({
items: dynamicRoutes.filter(r => r.isTab).map(r => ({
pagePath: r.path,
text: r.title,
iconPath: r.icon,
selectedIconPath: r.selectedIcon
}))
});
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn