• uni-simple-router


    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

    一、快速上手
    // 针对uniapp HBuilder创建的项目,非cli构建
    // 1⃣ NPM 安装
    npm install uni-simple-router
    
    // 2⃣ 初始化
    npm install uni-read-pages // 配合vue.config.js自动读取pages.json作为路由表的方式,源码例如下:扩二
    
    // 配置vue.config.js
    const TransformPages = require('uni-read-pages')
    const tfPages = new TransformPages()
    module.exports = {
    	configureWebpack: {
    		plugins: [
    		      new tfPages.webpack.DefinePlugin({
                          // 1⃣ 配置全局变量
                          // ROUTES: JSON.stringify(tfPages.routes) 
                          // 2⃣ includes 可自定义,解析pages.json路由配字段的配置, 默认 ['path', 'name', 'aliasPath']
                            ROUTES: tfPages.webpack.DefinePlugin.runtimeValue(()=>{
                              const tf = new TransformPages({
                                includes:  ['path', 'name', 'aliasPath']
                              })
                              return JSON.stringify(tfPages.routes)
                            },true)
    	              })
    		]
    	}
    }
    
    // /Applications/HBuilderX.app/Contents/HBuilderX/plugins/uniapp-cli/node_modules/webpack 我的webpack包路径,在软件contents包文件里软件自带的webpack
    
    扩一:webpack插件之DefinePlugin
    • 允许创建一个 在编译时可以配置的全局变量
    • 场景:区分不同开发模式处理
    // 1⃣ 用法:每个传进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")
    
    // 2⃣ 功能标记 来作为一个flag标识启用和禁用构建中的功能
    new webpack.DefinePlugin({
      'SHOW_PRESSION': JOSN.string(true)
    })
    
    // 3⃣ 服务:可以配置不同环境下的url
    new webpack.DefinePlugin({
      'DEV_URL': JSON.stringify(url_dev),
      'PRO_URL': JSON.stringify(url_pro)
    })
    
    扩二:uni-read-pages 如何获取pages.json中的路由
    // 依赖的源码 - 通过node的path模块获取pages.json文件中的任何信息 (部分是个人注释)
    const path = require('path')
    const CONFIG = { includes: ['path', 'aliasPath', 'name'] } // 默认获取路由参数属性
    // process.cwd() 返回Node.js进程的当前工作目录
    // path.resolve(url1, 'abc') => url1/abc
    const rootPath = path.resolve(process.cwd(), 'node_modules'); // 获取根路径
    
    /** 解析绝对路径
     * @param {Object} dir 
     */
    function resolvePath(dir) {
    	return path.resolve(rootPath, dir);
    }
    // 类
    class TransformPages {
    	constructor(config) {
        // 组合 自定义获取配置属性
    		config = { ...CONFIG, ...config }; 
    		this.CONFIG = config;
        
        // ↓本机文件路径(HBuilderX包文件里自带的webpack模块路径) /Applications/HBuilderX.app/Contents/HBuilderX/plugins/uniapp-cli/node_modules/webpack
    		this.webpack = require(resolvePath('webpack'));
    		this.uniPagesJSON = require(resolvePath('@dcloudio/uni-cli-shared/lib/pages.js'));
        
        // TODO: 根据CONFIG解析pages.json中的路由信息 和 小程序 分包下的路由
    		this.routes = this.getPagesRoutes().concat(this.getNotMpRoutes());
    	}
       // 获取所有pages.json下的内容 返回json
    	get pagesJson() {
    		return this.uniPagesJSON.getPagesJson();
    	}
    	
      // 通过读取pages.json文件 生成直接可用的routes
    	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
    	}
    	/**
    	 * 单条page对象解析
    	 * @param {Object} pageCallback 
    	 * @param {Object} subPageCallback
    	 */
    	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' }// 及其它自定义参数
          }
        }
      ]
    })
    // 组件中可以通过 this.$Route 查看路由元信息
    
    2.2 完全使用vue-router开发 (H5端)
    • 嵌套路由时,若使用name方式跳转,仅支持 this.$router.push({ name:  children1 })
    // 使用 vue-router开发将会失去uniapp的生命周期
    const router = createRouter({
      h5: {
        vueRouterDev: true, // 完全使用vue-router开发 默认是false
      },
      // 路由配置
      routes: [
        {
          path: '/',
         	name: 'home',
          component: () => import('@/common/router/home.vue'),
          // 嵌套路由(仅H5端),小程序端会重新页面打开
          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 路由传参
    // 除vue-router外,美化url可以在基础配置时,定义aliasPath变量,设置路由别名(浏览器地址栏展示名称)
    // 别名的设置,如果设置了别名,通过push路径的方式,必须使用aliasPath的路径才能生效
    const router = createRouter({
      routes:[{
            name:'router1',
            //为了兼容其他端,此时的path不能少,必须和 pages.json中的页面路径匹配
            path: "/pages/tabbar/tabbar-1/tabbar-1",    
            aliasPath: '/tabbar-1', 
        },]
    });
    
    // uni-simple-router路由跳转
    // aliasPath命名的路由 => name传递参数需 params
    this.$Router.push({ name: 'detail', params: { id: 1 } })
    // 带查询参数,变成 /home/id=1  => path 对应query,params失效
    this.$Router.push({ path: '/pages/index/detail', query: { id: 1 }})
    
    2.4 H5端路由捕获所有路由或404路由
    path:'*' // 通常用于客户端404错误,如果是history模式,需要正确配置服务器
    path: '/detail-*'
    // 路由的优先级根据 定义的先后
    
    2.5 路由懒加载
    • 打包构建应用时,Javascript包会变得非常大,影响页面加载,把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件。

    •   const Foo = () => import('./Foo.vue')
        // 把组件按组分块 使用 命名 chunk
        const Foo = () => import(/*webpackChunkName:"group-foo"*/ './Foo.vue')
        const Bar = () => import(/*webpackChunkName:"group-foo"*/ './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也提供了类似的组件,需要手动注册

    •   // main.js
        import Link from './node_modules/uni-simple-router/dist/link.vue'
        Vue.component('Link', Link)
      
    •   // 通过path直接跳转 并指定跳转类型
        <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不接受;传递深度对象参数时,需要先编码再传递解码

    •   // 动态改变参数 使 onLoad和onShow支持options
        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();
        }
      })
      
    • 全局后置守卫

      // 不接受next函数也不改变导航本身
      router.afterEach((to,from) => {})
      
    • 路由独享守卫

      // 路由配置上直接定义beforeEnter守卫
      const router = createRouter({
        routes: [{
          path: '/pages/home/index',
          beforeEnter:(to,from,next) => {
           // 参数同上全局前置守卫 
            next()
          }
        }]
      })
      
    • 组件内的守卫

      // 组件内配置beforeRouteLeave守卫 - 直接调用beforeRouteLeave方法
      export default {
        beforeRouteLeave(to, from, next) {
          // 导航离开该组件的对应路由时调用
          // 可以访问组件实例this
          next()
        }
      }
      
    六、路由守卫-模块化
    // 1⃣ 创建 router文件夹,模块化配置 文件结构
    |+------------------------+|
    | router                   |
    | |+--------------------+| |
    | | modules              | |
    | | |+----------------+| | |
    | | | home.js          | | |
    | | | index.js         | | |
    | | |+----------------+| | |
    | |+--------------------+| |
    | index.js                 |
    |+------------------------+|
    
    // home.js
    const home = [
      {
        path: '/pages/home/index',
        name: 'home'
      }
    ]
    export default home
    
    // modules下的index.js是一个模块读取
    // ① require.context(directory, useSubdirectories, regExp) 具体详情:如下扩三
    const files = require.contxt('.',false,/.js$/)
    const modules = []
    
    files.keys().froEach(key => {
      if (key === './index.js') return
      const item = files(key).default
      modules.push(...item)
    })
    
    export default modules // 将所有模块的路由模块整合到一起, routes Array
    
    // router下的index.js 路由守卫
    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 //默认true
     },
     h5: {
      vueRouterDev: true, //完全使用vue-router开发 默认 false  
     },
     // 也可以 通过uni-read-pages来读取pages.json文件的路由表,配合vue.config.js
     // router: [...ROUTES] // ROUTES是通过webpack的defaultPlugin编译成全局变量,具体操作上文
     routes: [...modules] //路由表
    });
    
    //全局路由前置守卫
    router.beforeEach((to, from, next) => {
     // 首先判断是否存在路由信息
     //不存在就先调用接口得到数据
    })
    // 全局路由后置守卫
    router.afterEach((to, from) => {})
    export default router;
    
    扩三、require.context用法
    // require.context(directory, useSubdirectories, regExp)
    // directory: 表示检索的目录
    // useSubdirectories: 表示是否检索子文件夹
    // regExp: 匹配文件的正则表达式
    // 返回值: resolve是一个函数,返回已解析请求的模块ID; keys是一个函数,它返回上下文模块可以处理的所有可能请求的数组;
    // 使用场景:①用来组件内引入多个组件;②在main.js内引入大量公共组件;
    
    // ①组件内引入多个组件 - webpack
    const path = require('path')
    const files = require.context('@/components/home', false, /\.vue$/) //值类型 ['./home.js', 'detail.js',...]
    const modules = {}
    
    files.keys().forEach(key => {
      const name = paths.basename(key, '.vue') // 去掉文件名的 .vue后缀
      modules[name] = files(key).default || files(key)
    })
    // modules { home: '{module样式路径}', detail: '{}', ...  }
    export default { 
      	...,
      	data() { return {}}, 
        components: modules
    }
    
    // ②在main.js内引入大量公共组件
    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)
    })
    
  • 相关阅读:
    SpringBoot_整合Mybatis
    vscode 命令无法执行
    c语言练习题79:设计停⻋系统
    01数据结构与算法刷题之【链表】篇
    mac pro M1(ARM)安装:centos8.0虚拟机
    vue3 +element-plus中避免一打开表单的下拉选择的change事件自动校验问题
    super关键字
    MySql 数据库【Union、Limit】
    数据结构-作业9
    2023最新SSM计算机毕业设计选题大全(附源码+LW)之java高校心理咨询管理系统0e78p
  • 原文地址:https://www.cnblogs.com/xsk-walter/p/16304853.html