目录
- uni-simple-router
- 一、快速上手
- 扩一:webpack插件之DefinePlugin
- 扩二:uni-read-pages 如何获取pages.json中的路由
- 二、H5模式
- 2.1 路由配置
- 2.2 完全使用vue-router开发 (H5端)
- 2.3 H5 路由传参
- 2.4 H5端路由捕获所有路由或404路由
- 2.5 路由懒加载
- 三、小程序模式
- 四、路由跳转
- 4.1 组件跳转
- 4.2 编程式导航
- 五、跨平台模式
- 5.1 提前享用生命周期
- 5.2 导航守卫
- 六、路由守卫-模块化
- 扩三、require.context用法
- 总结
uni-simple-router
- 专为uniapp打造的路由器,和uniapp深度集成
- 通配小程序、App和H5端
- H5能完全使用vue-router开发
- 模块化、查询、通配符、路由参数
- 使 uni-app实现嵌套路由(仅H5端完全使用vue-router)
- uniapp用到了很多vue的api,但在路由管理的功能相对于vue-router还是比较欠缺的,比如全局导航守卫
官方文档:https://hhyang.cn/v2/start/quickstart.html
一、快速上手
| |
| |
| npm install uni-simple-router |
| |
| |
| npm install uni-read-pages |
| |
| |
| const TransformPages = require('uni-read-pages') |
| const tfPages = new TransformPages() |
| module.exports = { |
| configureWebpack: { |
| plugins: [ |
| new tfPages.webpack.DefinePlugin({ |
| |
| |
| |
| ROUTES: tfPages.webpack.DefinePlugin.runtimeValue(()=>{ |
| const tf = new TransformPages({ |
| includes: ['path', 'name', 'aliasPath'] |
| }) |
| return JSON.stringify(tfPages.routes) |
| },true) |
| }) |
| ] |
| } |
| } |
| |
| |
扩一:webpack插件之DefinePlugin
- 允许创建一个 在编译时可以配置的全局变量
- 场景:区分不同开发模式处理
| |
| new webpack.DefinePlugin({ |
| PRODUCTION: JSON.stringify(true), |
| BROWSER_SUPPRTS_HTML5: true, |
| VERSION: JSON.stringify('abcde'), |
| TWO: '1+1', |
| 'typeof window': JSON.stringify('object') |
| }) |
| |
| console.log('Running App version', VERSION) |
| if(!BROWSER_SUPPRTS_HTML5) require("html5shiv") |
| |
| |
| new webpack.DefinePlugin({ |
| 'SHOW_PRESSION': JOSN.string(true) |
| }) |
| |
| |
| new webpack.DefinePlugin({ |
| 'DEV_URL': JSON.stringify(url_dev), |
| 'PRO_URL': JSON.stringify(url_pro) |
| }) |
扩二:uni-read-pages 如何获取pages.json中的路由
| |
| const path = require('path') |
| const CONFIG = { includes: ['path', 'aliasPath', 'name'] } |
| |
| |
| const rootPath = path.resolve(process.cwd(), 'node_modules'); |
| |
| |
| |
| |
| function resolvePath(dir) { |
| return path.resolve(rootPath, dir); |
| } |
| |
| class TransformPages { |
| constructor(config) { |
| |
| config = { ...CONFIG, ...config }; |
| this.CONFIG = config; |
| |
| |
| this.webpack = require(resolvePath('webpack')); |
| this.uniPagesJSON = require(resolvePath('@dcloudio/uni-cli-shared/lib/pages.js')); |
| |
| |
| this.routes = this.getPagesRoutes().concat(this.getNotMpRoutes()); |
| } |
| |
| get pagesJson() { |
| return this.uniPagesJSON.getPagesJson(); |
| } |
| |
| |
| getPagesRoutes(pages = this.pagesJson.pages, rootPath = null) { |
| const routes = []; |
| for (let i = 0; i < pages.length; i++) { |
| const item = pages[i]; |
| const route = {}; |
| for (let j = 0; j < this.CONFIG.includes.length; j++) { |
| const key = this.CONFIG.includes[j]; |
| let value = item[key]; |
| if (key === 'path') { |
| value = rootPath ? `/${rootPath}/${value}` : `/${value}` |
| } |
| if (key === 'aliasPath' && i == 0 && rootPath == null) { |
| route[key] = route[key] || '/' |
| } else if (value !== undefined) { |
| route[key] = value; |
| } |
| } |
| routes.push(route); |
| } |
| return routes; |
| } |
| |
| |
| getNotMpRoutes() { |
| const { subPackages } = this.pagesJson; |
| let routes = []; |
| if (subPackages == null || subPackages.length == 0) { |
| return []; |
| } |
| for (let i = 0; i < subPackages.length; i++) { |
| const subPages = subPackages[i].pages; |
| const root = subPackages[i].root; |
| const subRoutes = this.getPagesRoutes(subPages, root); |
| routes = routes.concat(subRoutes) |
| } |
| return routes |
| } |
| |
| |
| |
| |
| |
| parsePages(pageCallback, subPageCallback) { |
| this.uniPagesJSON.parsePages(this.pagesJson, pageCallback, subPageCallback) |
| } |
| } |
| module.exports = TransformPages |
二、H5模式
2.1 路由配置
| import {RouterMount,createRouter} from 'uni-simple-router'; |
| const router = createRouter({ |
| // 路由配置 |
| routes: [ |
| { |
| path: '/pages/index/index', // 必须和pages.json中相同 |
| extra: { |
| pageStyle: { color: '#f00' }// 及其它自定义参数 |
| } |
| } |
| ] |
| }) |
| |
2.2 完全使用vue-router开发 (H5端)
嵌套路由时,若使用name方式跳转,仅支持 this.$router.push({ name: children1 })
| |
| const router = createRouter({ |
| h5: { |
| vueRouterDev: true, // 完全使用vue-router开发 默认是false |
| }, |
| // 路由配置 |
| routes: [ |
| { |
| path: '/', |
| name: 'home', |
| component: () => import('@/common/router/home.vue'), |
| |
| children: [ |
| { |
| path: 'home/children1', |
| name: 'children1', |
| component: () => import('@/common/router/children1.vue') |
| }, |
| { |
| path: 'home/children2', |
| name: 'children2', |
| component: () => import('@/common/router/children2.vue') |
| } |
| ] |
| } |
| ] |
| }) |
2.3 H5 路由传参
| |
| |
| const router = createRouter({ |
| routes:[{ |
| name:'router1', |
| //为了兼容其他端,此时的path不能少,必须和 pages.json中的页面路径匹配 |
| path: "/pages/tabbar/tabbar-1/tabbar-1", |
| aliasPath: '/tabbar-1', |
| },] |
| }); |
| |
| |
| |
| this.$Router.push({ name: 'detail', params: { id: 1 } }) |
| |
| this.$Router.push({ path: '/pages/index/detail', query: { id: 1 }}) |
2.4 H5端路由捕获所有路由或404路由
| path:'*' |
| path: '/detail-*' |
| |
2.5 路由懒加载
打包构建应用时,Javascript包会变得非常大,影响页面加载,把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件。
| const Foo = () => import('./Foo.vue') |
| |
| const Foo = () => import( './Foo.vue') |
| const Bar = () => import( './Bar.vue') |
三、小程序模式
- 注:小程序系列无法拦截原生tabbar及原生导航返回,如需拦截使用自定义tabbar、header
- 通过api进行切换时,像uni.switchTab()和this.$Router.pushTab()方法会触发拦截的;仅底部原生tabbar进行切换时不触发拦截
- 强制触发守卫:forceGuardEach(replaceAll, false) 每次调用api都会重新按流程触发已经声明的所有守卫
- 小程序端默认:插件api跳转、uni导航api跳转和首屏加载
- 使用路由守卫:通过点击事件强制触发、混入到onshow回调触发
- 跳转路由锁:animationDuration保留给redirection\push足够时间,等切换完成页面后才放行下次跳转
| const router = createRouter({ |
| platform: process.env.VUE_APP_PLATFORM, |
| // ① 路由锁 |
| applet: { |
| animationDuration: 300 // 默认300ms |
| // animationDuration: 0 // 不精准 只捕捉跳转api下的complete函数 |
| }, |
| // ②优雅解锁 error.type: 0 表示 next(false)、1表示next(unknownType)、2表示加锁状态,禁止跳转、3表示在获取页面栈时,页面栈不够level获取 |
| routerErrorEach:(error, router) => { |
| if (error.type === 3) { |
| router.$lockStatus = false |
| } |
| }, |
| routes: [...ROUTES] |
| }) |
四、路由跳转
4.1 组件跳转
vue-router中可以通过router-link组件进行页面跳转,uni-simple-router也提供了类似的组件,需要手动注册
| |
| import Link from './node_modules/uni-simple-router/dist/link.vue' |
| Vue.component('Link', Link) |
| |
| <Link to="/tabbar1" navType="pushTab"> |
| <button type="primary">使用path对象跳转</button> |
| </Link> |
4.2 编程式导航
- 通过this.$Router获取路由对象;push、pushTab、replace、back等api进行路由跳转
- 注:path搭配query参数、name搭配params参数
- 导航使用方式同vue-router
五、跨平台模式
5.1 提前享用生命周期
uniapp由于只用onLoad接受options参数、onShow不接受;传递深度对象参数时,需要先编码再传递解码
| |
| const router = createRouter({ |
| platform: process.env.VUE_APP_PLATFORM, |
| routes: [...ROUTES], |
| beforeProxyHooks: { |
| onLoad(options, next){ |
| next([router.currentRoute.query]); |
| }, |
| onShow([options], next){ |
| console.log(this); |
| const args=options||router.currentRoute.query; |
| next([args]); |
| }, |
| }, |
| }); |
5.2 导航守卫
全局前置守卫
| /** |
| * to: Route 即将进入的目标 |
| * from: Route 当前导航正要离开的路由 |
| * next: Function 该方法的resolve钩子函数必须调用,执行效果依赖next方法的调用参数 |
| * -- next()调用参数:管道中的下个钩子; next(false)中断当前导航; |
| * -- next('/')/({path: '/'})跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航 |
| * -- next({delta: 2, NAVTYPE: 'back'}) 中断当前导航,调用新的跳转类型,同时返回两层页面 |
| **/ |
| router.beforeEach((to, from, next) => { |
| // ... |
| // 1⃣ next() |
| // 2⃣ NAVTYPE定义跳转方式,两者相同非必填 |
| if (to.name == 'tabbar-5') { |
| next({ |
| name: 'router4', |
| params: { |
| msg: '我拦截了tab5并重定向到了路由4页面上', |
| }, |
| NAVTYPE: 'push' |
| }); |
| } else{ |
| next(); |
| } |
| }) |
全局后置守卫
| |
| router.afterEach((to,from) => {}) |
路由独享守卫
| |
| const router = createRouter({ |
| routes: [{ |
| path: '/pages/home/index', |
| beforeEnter:(to,from,next) => { |
| |
| next() |
| } |
| }] |
| }) |
组件内的守卫
| |
| export default { |
| beforeRouteLeave(to, from, next) { |
| |
| |
| next() |
| } |
| } |
六、路由守卫-模块化
| |
| |+------------------------+| |
| | router | |
| | |+--------------------+| | |
| | | modules | | |
| | | |+----------------+| | | |
| | | | home.js | | | |
| | | | index.js | | | |
| | | |+----------------+| | | |
| | |+--------------------+| | |
| | index.js | |
| |+------------------------+| |
| |
| const home = [ |
| { |
| path: '/pages/home/index', |
| name: 'home' |
| } |
| ] |
| export default home |
| |
| |
| const files = require.context('.',false,/.js$/) |
| const modules = [] |
| |
| files.keys().forEach(key => { |
| if (key === './index.js') return |
| const item = files(key).default |
| modules.push(...item) |
| }) |
| |
| export default modules |
| |
| import modules from './modules/index.js' |
| import Vue from 'vue' |
| import CreateRouter from 'uni-simple-router' |
| import store from '@/store/store.js' |
| |
| Vue.use(CreateRouter) |
| |
| const router = new CreateRouter({ |
| APP: { |
| holdTabbar: false |
| }, |
| h5: { |
| vueRouterDev: true, |
| }, |
| |
| |
| routes: [...modules] |
| }); |
| |
| |
| router.beforeEach((to, from, next) => { |
| |
| |
| }) |
| |
| router.afterEach((to, from) => {}) |
| export default router; |
扩三、require.context用法
| |
| |
| |
| |
| |
| |
| |
| const path = require('path') |
| const files = require.context('@/components/home', false, /\.vue$/) |
| const modules = {} |
| |
| files.keys().forEach(key => { |
| const name = paths.basename(key, '.vue') |
| modules[name] = files(key).default || files(key) |
| }) |
| |
| export default { |
| ..., |
| data() { return {}}, |
| components: modules |
| } |
| |
| import Vue from 'vue' |
| |
| const requireComponents = require.context('../views/components',true,'/\.vue/') |
| |
| requireComponents.keys().forEach(fileName => { |
| const reqCom = requireComponents(fileName) |
| |
| const reqComName = reqCom.name || fileName.replace(/\.\/(.*)\.vue/, '$1') |
| |
| Vue.components(reqComName, reqCom.default || reqCom) |
| }) |