195  
查询码:00001152
uni-app路由的封装(最新版本请看github) 更新内容
作者: 朱凡 于 2020年03月15日 发布在分类 / FM组 / FM_App 下,并于 2020年03月15日 编辑

uni-app路由的封装(最新版本请看github)

https://gitee.com/zhufan/min-router

更新内容

  1.   配置路由
    1. 此前版本需要手动配置路由,版本更新后将自动生成路由配置
  2. 添加全局钩子
    1. 此前不加全局钩子是为了性能
    2. 更新后会添加对应的钩子
  3. (=&?)等特殊字符,将不会支持路由传参,各位可自行处理
    1. encodeURIComponent
    2. decodeURIComponent
  4. 新版Router使用方式

    在页面添加 navigate类型是 Array 对应值是:['navigateTo', 'switchTab', 'reLaunch', 'redirectTo']

    用法

    // 文件目录 pages/home/index.vue export default {
      navigate: ['navigateTo']
    }
    // 会生成 navigateTo 跳转的方式
    // 会生成 name 值为 所在文件夹的名称(home) export default {
      navigate: ['navigateTo', 'switchTab']
    }
    // 会生成navigateTo、switchTab跳转的方式
    // 会生成两条数据
    // navigateTo 对应 name 值为 所在文件夹的名称(home)
    // switchTab 对应 name 值为 所在文件夹的名称(swhome)
    
    // 其他方式对应值
    // reLaunch 对应 name 值为 所在文件夹的名称(relhome)
    // redirectTo 对应 name 值为 所在文件夹的名称(redhome) 复制代码
    this.$minRouter.push({
    	// 这里的 name 对应上面生成的 name 值
        name: 'home',
        // params 是页面传参类型最好是 Object
        params: { id: 1 }
    })
    
    
    // 不传参时可以直接如下写法
    this.$minRouter.push('swhome')
    
    
    // 获取页面参数
    this.$parseURL()
    
    // 回退页面
    this.$minRouter.back(-1)复制代码

    项目中文件目录 router/index.js

    可以设置路由拦截用法和 Vue 全局前置钩子类似

  5. 具体看 github


uni-app提供了路由功能来实现页面跳转,但是在使用的过程中我们还是发现有些不方便的地方,如下图:

页面的跳转存在哪些问题呢

  1. 页面路径不好的管理问题
    1. 为什么不好管理:就是改页面路径,或者说分包的时候不用一个个找着改
  2. 传递参数的方式不太友好
    1. 为什么不友好:需要手动拼接参数列表
  3. 参数类型单一,只支持string
    1. 不管传string还是number和boolean得到的都是字符串
  4. 传特殊字符时会发现参数被截断(比如传二维码信息)
    1. 原因是不能包括(=&?)等特殊字符
首先先做一下准备工作

    创建一个项目


第一个问题好解决,先做一个集中管理,创建一个文件(router.js)来集中管理,目录结构下

代码如下

export default {
  // 首页页面
  index: '/pages/index/index.vue',
  // 我的页面
  my: '/pages/my/index.vue' }复制代码

使用的时候变成这样

<template>
  <view class="content">
    <image class="logo" src="/static/logo.png"></image>
    <view>
      <text class="title">{{title}}</text>
    </view>
    <button @click="openPage">跳转到我的页面</button>
  </view>
</template>
<script>
  import url from '../../router' export default { data() { return {
        title: 'index' }
    }, onLoad() {    },
    methods: { openPage () {
        uni.navigateTo({
          url: url.my
        })
      }
    }
  }
</script>复制代码

使用的时候需要引入router.js特别麻烦,先不解决这个问题

下面来看第二和第三个问题

先看个例子

参数比较多的情况下这样确实不好操作麻烦,能不能把参数部分单独拿出来然后拼接到url上面呢?

惊奇的发现传过来的数字竟然变成字符串了,参数不能保真

第四个问题就不演示了

废话不多说了,说了这么多,相信你们也应该知道问题的所在了,下面就来解决这些问题 (渐进式讲解

首先创建一个文件(MinRouter.js)目录结构如下


下面代码解决了第一个问题

不再需要使用是引入router.js文件

import urlSet from './router'; function openPage (url) {  
    uni.navigateTo({
        url: `${urlSet[url]}`
    })
} export default openPage复制代码

main.js文件做如下修改

import Vue from 'vue' import App from './App' // 引入MinRouter文件
import openPage from './MinRouter' Vue.config.productionTip = false App.mpType = 'app' // 添加到全局
global.openPage = openPage
const app = new Vue({
    ...App
})
app.$mount()复制代码

使用方式

<template>
  <view class="content">
      <image class="logo" src="/static/logo.png"></image>
      <view>
          <text class="title">{{title}}</text>
      </view>
      <button @click="toPage">跳转到我的页面</button>
</view>
</template>
<script> export default { data() { return {
            title: 'index' }
    }, onLoad() {    },
    methods: { toPage () {
        global.openPage('my')
      }    
 }  
}
</script>复制代码

下面来解决第二和第三个问题

第三个问题想必很多人都遇到过,本来就想传递一个number,结果不管传什么都会变成string。

有什么办法可以让数据变成字符串之后,还能还原成原来的类型?

使用JSON就能解决上面的问题了,而且也很好的解决了第二个问题

试着修改原来的代码

import urlSet from './router'; function openPage (url, query) {
  const queryStr = JSON.stringify(query)
  uni.navigateTo({
    url: `${urlSet[url]}?query=${queryStr}`
  })} export default openPage复制代码

使用方式

<template>
  <view class="content">
    <image class="logo" src="/static/logo.png"></image>
    <view>
      <text class="title">{{title}}</text>
    </view>
    <button @click="toPage">跳转到我的页面</button>
  </view></template><script> export default { data() { return {
        title: 'index' }
    }, onLoad() {    },
    methods: { toPage () {
        global.openPage('my', {id: 123})
      }
    }
  }
</script>复制代码

下面来解决第四个问题

(=&?)等特殊字符在url上面是有特殊含义的,所以我们要把json字符串encode一下

import urlSet from './router';
function openPage (url, query) {
  const queryStr = encodeURIComponent(JSON.stringify(query))
  uni.navigateTo({
    url: `${urlSet[url]}?query=${queryStr}`
  })}
export default openPage复制代码

到此上面的问题全部解决了,但是感觉还是不太好,能不能封装成Vue插件,类似VueRouter

答案是肯定的

封装成型MinRouter

router.js文件改成如下

import MinRouter from './MinRouter' // 配置路由
const router = new MinRouter({
  routes: [
    {
      // 页面路径
      path: 'pages/index/index',
      name: 'index' },
    {
      path: 'pages/my/index',
      name: 'my' }
  ]
 }) export default router复制代码

main.js文件改成如下

import Vue from 'vue' import App from './App' // 引入MinRouter文件
import MinRouter from './MinRouter' // 引入router文件
import minRouter from './router' Vue.config.productionTip = false // 注册插件
Vue.use(MinRouter)
App.mpType = 'app' const app = new Vue({
    ...App,
    minRouter
})
app.$mount()复制代码

上面的代码配置中已经很像VueRouter了

在MinRouter文件添加以下代码

const toString = Object.prototype.toStringfunction
 isObject (value) { return toString.call(value) === '[object Object]' } function isString (value) { return toString.call(value) === '[object String]
'} function isDefault (value) { return value === void 0
} function install (Vue) {
  Vue.mixin({
    beforeCreate: function () { if (!isDefault(this.$options.minRouter)) {
        Vue._minRouter = this.$options.minRouter
      }    
}  
})  
Object.defineProperty(Vue.prototype, '$minRouter', {
    get: function () { return Vue._minRouter._router
    }
  })
} function MinRouter (options) { if (!(this instanceof MinRouter)) {
    throw Error("MinRouter是一个构造函数,应该用`new`关键字调用")
  }
  isDefault(options) && (options = {})
  this.options = options
  this._router = options.routes || []
}
MinRouter.install = install export default MinRouter复制代码

下面来设定openPage的参数

name:表示要跳转的页面

query:跳转页面所带的参数

调用方式:   openPage({name: 跳转的页面, query: {id: 123}})复制代码

openPage函数如下

function openPage (args) { let {name, query = {}} = args let queryStr = null, path
  queryStr = encodeURIComponent(JSON.stringify(query))
  this.$minRouter.forEach(item => { if (item.name === name) {
      path = item.path
    }
  }) return new Promise((resolve, reject) => {
    uni.navigateTo({
      url: `/${path}?query=${queryStr}`,
      success: resolve,
      fail: reject
    })
  })
}复制代码
this.$minRouter已经在上面的代码代理过来了,不要觉得奇怪,其实就是配置路由中的routes复制代码

上面的只能的路由只能使用这种方式navigateBack

这样肯定是不行的,你们可能会想在加一个参数去控制路由跳转方式,但是这样觉得不是很简便,既然openPage函数不能加,能不能在路由里面加了?

下面来修改router.js文件

import MinRouter from './MinRouter' // 配置路由
const router = new MinRouter({
  routes: [
    {
      // 页面路径
      path: 'pages/index/index',
      // type必须是以下的值['navigateTo', 'switchTab', 'reLaunch', 'redirectTo']
      // 跳转方式(默认跳转方式) type: 'navigateTo',
      name: 'index' },
    {
      path: 'pages/my/index',
      name: 'my' }
  ]
 }) export default router复制代码

openPage函数如下

function openPage (args) { let name, query = {}, queryStr = null, path, type switch (true) { case isObject(args):
      ({name, query = {}} = args) break case isString(args):
      name = args break default:
      throw new Error('参数必须是对象或者字符串')
  } if (isObject(query)) {
    queryStr = encodeURIComponent(JSON.stringify(query))
  } else {
    throw new Error('query数据必须是Object')
  }
  this.$minRouter.forEach(item => { if (item.name === name) {
      path = item.path type = item.type || 'navigateTo' }
  }) if (!['navigateTo', 'switchTab', 'reLaunch', 'redirectTo'].includes(type)) {
    throw new Error(`name:${name}里面的type必须是以下的值['navigateTo', 'switchTab', 'reLaunch', 'redirectTo']`)
  } return new Promise((resolve, reject) => {
    uni[type]({
      url: `/${path}?query=${queryStr}`,
      success: resolve,
      fail: reject
    })
  })
}复制代码

说了这么多还没说怎么解析路由参数了

下面函数是解析路由参数的

function parseURL () {
  const query = this.$root.$mp.query.query if (query) { return JSON.parse(decodeURIComponent(query))
  } else { return {}
  }
}复制代码

以下是完整的MinRouter代码

const toString = Object.prototype.toString function isObject (value) { return toString.call(value) === '[object Object]' } function isString (value) { return toString.call(value) === '[object String]' } function isDefault (value) { return value === void 0
} function openPage (args) { let name, query = {}, queryStr = null, path, type, isName = false switch (true) { case isObject(args):
      ({name, query = {}} = args) break case isString(args):
      name = args break default:
      throw new Error('参数必须是对象或者字符串')
  } if (isObject(query)) {
    queryStr = encodeURIComponent(JSON.stringify(query))
  } else {
    throw new Error('query数据必须是Object')
  }
  this.$minRouter.forEach(item => { if (item.name === name) {
      path = item.path type = item.type || 'navigateTo' isName = true }
  }) if (!isName) {
    throw new Error(`没有${name}页面`)
  } if (!['navigateTo', 'switchTab', 'reLaunch', 'redirectTo'].includes(type)) {
    throw new Error(`name:${name}里面的type必须是以下的值['navigateTo', 'switchTab', 'reLaunch', 'redirectTo']`)
  } return new Promise((resolve, reject) => {
    uni[type]({
      url: `/${path}?query=${queryStr}`,
      success: resolve,
      fail: reject
    })
  })
} function parseURL () {
  const query = this.$root.$mp.query.query if (query) { return JSON.parse(decodeURIComponent(query))
  } else { return {}
}} function install (Vue) {
  Vue.mixin({
    beforeCreate: function () { if (!isDefault(this.$options.minRouter)) {
        Vue._minRouter = this.$options.minRouter
      }
    }
  })
  Object.defineProperty(Vue.prototype, '$minRouter', {
    get: function () { return Vue._minRouter._router
    }
  })
  Object.defineProperty(Vue.prototype, '$parseURL', {
    get: function () { return Vue._minRouter.parseURL
    }
  })
  Object.defineProperty(Vue.prototype, '$openPage', {
    get: function () { return Vue._minRouter.openPage
    }
  })} function MinRouter (options) { if (!(this instanceof MinRouter)) {
    throw Error("MinRouter是一个构造函数,应该用`new`关键字调用")
  }
  isDefault(options) && (options = {})
  this.options = options
  this._router = options.routes || []
}
MinRouter.install = install
MinRouter.prototype.openPage = openPage
MinRouter.prototype.parseURL = parseURL export default MinRouter复制代码

使用方式如下

<template>
  <view class="content">
    <image class="logo" src="/static/logo.png"></image>
    <view>
      <text class="title">{{title}}</text>
    </view>
    <button @click="toPage">跳转到我的页面</button>
  </view>
</template>
<script> export default { data() { return {
        title: 'index' }
    }, onLoad() {
      // 解析路由参数
      console.log(this.$parseURL())
    },
    methods: { toPage () {
        // 跳到my的页面  query是传递的参数
        this.$openPage({
          name: 'my',
          query: {id: 123}
        })
      }
    }
  }
</script>复制代码
<template>
  <view class="content">
    <image class="logo" src="/static/logo.png"></image>
    <view>
      <text class="title">{{title}}</text>
    </view>
    <button @click="toPage">跳转到首页页面</button>
  </view>
</template>
<script> export default { data() { return {
        title: 'my' }
    }, onLoad() {
      // 解析路由参数
      console.log(this.$parseURL())
    },
    methods: { toPage () {
        // 跳到index的页面
        // 不传参数可以简写成如下
        this.$openPage('index')
      }
    }
  }
</script>


 推荐知识

 历史版本

修改日期 修改人 备注
2020-03-15 19:31:39[当前版本] 朱凡 创建版本

 附件

附件类型

GIFGIF

  目录
    知识分享平台 -V 4.8.7 -wcp