uniapp用到了vue的很多api,但在路由管理的功能上相较于vue-router还是比较欠缺的,比如全局导航守卫。

我们可以通uniapp的插件uni-simple-router来实现类似于vue-router的功能,但多端兼容时,一些用法还需要注意,我们会讲到。

一、安装

如果你的项目没有使用package,请先初始化:

$ npm init -y

安装依赖:

$ npm install uni-simple-router uni-read-pages

二、配置与初始化

1、根目录新建 vue.config.js 文件,写入以下内容:

//vue.config.js
const TransformPages = require('uni-read-pages')
const {webpack} = new TransformPages()
module.exports = {
    configureWebpack: {
        plugins: [
            new webpack.DefinePlugin({
                ROUTES: webpack.DefinePlugin.runtimeValue(() => {
                    const tfPages = new TransformPages({
                        includes: ['path', 'name', 'aliasPath']
                    });
                    return JSON.stringify(tfPages.routes)
                }, true )
            })
        ]
    }
}

⚠️ 其中要重点关注第 10 行:

// ...
    includes: ['path', 'name', 'aliasPath']
// ...

includes 中包含的是router会读取pages路由中的字段名,后续如果有用到meta等路由信息,可以在 includes 里增加 'meta',在pages路由中写对应的数据,router中就可以获取得到(后面再补充案例)

2、根目录新建并写入router.js,写入以下内容:

// router.js
import {RouterMount,createRouter} from 'uni-simple-router';

const router = createRouter({
    platform: process.env.VUE_APP_PLATFORM,
    routes: [...ROUTES]
});
//全局路由前置守卫
router.beforeEach((to, from, next) => {
    next();
});
// 全局路由后置守卫
router.afterEach((to, from) => {
    console.log('跳转结束')
})

export {
    router,
    RouterMount
}

3、main.js导入router.js并挂载

// main.js

import Vue from 'vue'
import App from './App'
import {router,RouterMount} from './router.js'  //路径换成自己的
Vue.use(router)

App.mpType = 'app'
const app = new Vue({
    ...App
})

//v1.3.5起 H5端 你应该去除原有的app.$mount();使用路由自带的渲染方式
// #ifdef H5
    RouterMount(app,router,'#app')
// #endif

// #ifndef H5
    app.$mount(); //为了兼容小程序及app端必须这样写才有效果
// #endif

4、重新编译运行

三、路由跳转

方法一:组件跳转

vue-router中可以通过 <router-link></router-link> 组件进行页面跳转,uni-simple-router也提供了类似的组件:只不过这个组件需要手动注册

// main.js
import Mylink from './node_modules/uni-simple-router/dist/link.vue'
Vue.component('my-link',Mylink)
<!-- 通过path直接跳转 并指定跳转类型 -->
<my-link to="/tabbar-4" navType="pushTab">
  <button type="primary">使用path对象跳转</button>
</my-link>

方法二:编程式导航:

在vue示例中,通过 this.$Router 获取路由对象(R必须大写),编程式导航与vue-router很接近,但仍有需要注意的地方,具体可以参考文档.

但是在uniapp中就要留意了,如果你要使用name进行跳转,那就无法携带query参数!同理,使用path跳转也不能使用params参数

// 以下是错误的写法,name不能搭配query使用,path也不能搭配params参数使用
this.$Router.push({ name: 'newsDetail', query: { id: '123' }})
this.$Router.push({ path: '/pages/news/detail', params: { id: '123' }})

// 以下是正确的写法:
this.$Router.push({ name: 'newsDetail', params: { id: '123' } })
this.$Router.push({ path: '/pages/news/detail',query: { id: '123' }})

总之:记住:path搭配query参数,name搭配params参数

四、APP退出应用

在app路由栈已到达最底层时,再次点击退出程序是非常常见的功能,我们可以这样实现:

// router.js文件

import {
    RouterMount,
    createRouter,
    runtimeQuit
} from './dist/uni-simple-router.js';
// runtimeQuit记得导入

const router = createRouter({
    platform: process.env.VUE_APP_PLATFORM,
    routerErrorEach:({type,msg})=>{
        console.log({type,msg})
        // #ifdef APP-PLUS
            if(type===3){
                router.$lockStatus=false;
                runtimeQuit();
            }
        // #endif
    },
    routes: [...ROUTES]
});

routerErrorEach中,type的含义如下:

  • 0 表示 next(false)
  • 1 表示 next(unknownType)
  • 2 表示加锁状态,禁止跳转
  • 3 表示在获取页面栈的时候,页面栈不够level获

除了再次点击退出外,你还可以实现自己的逻辑,比如弹窗提示退出等:

const router = createRouter({
    platform: process.env.VUE_APP_PLATFORM,
    routerErrorEach:({type,msg})=>{
        console.log({type,msg})
        // #ifdef APP-PLUS
            if(type===3){
                router.$lockStatus=false;
                uni.showModal({
                            title: '提示',
                            content: '您确定要退出应用吗?',
                            success: function (res) {
                                if (res.confirm) {
                                    plus.runtime.quit();
                                }
                            }
                        });
            }
        // #endif
    },
    routes: [...ROUTES]
});

五、Route对象

和vue-router一样,uniapp在页面中也能获取当前的页面路由信息,不过首字母改成了大写字母:

onLoad() {
    console.log(this.$Route)
}

$Route 中包含了路由的基本信息以及,vue.config.js配置中includes配置的字段,和导航守卫中的to/from对象一致,利用includes配置项和导航守卫,可以实现权限校验的配置:

// router.js
router.beforeEach((to, from, next) => {
    if (to.meta && to.meta.power === "public") { // 公共页面,不需要登录

    } else { // 需要登录的页面
             let isLogin = checkLogin(); // 判断是否登录
        if (isLogin) {
            next();
        } else {
            next({
                name: "login"
            })
        }
    }
});
// pages.json
"pages": [
        {
            "path": "pages/login/login",
            "name": "login",
            "desc": "登录页",
            "meta": {
                "power": "public" // 不需要登录
            }
        },
          { // 需要登录
            "path": "pages/my/my",
            "name": "login",
            "desc": "我的"
        }
          //...
  ]
03-05 15:24