uni-app路由的封装
更新内容
- 配置路由
- 此前版本需要手动配置路由,版本更新后将自动生成路由配置
- 添加全局钩子
- 此前不加全局钩子是为了性能
- 更新后会添加对应的钩子
- (=&?)等特殊字符,将不会支持路由传参,各位可自行处理
- encodeURIComponent
- decodeURIComponent
-
新版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
全局前置钩子类似 -
具体看 github
uni-app提供了路由功能来实现页面跳转,但是在使用的过程中我们还是发现有些不方便的地方,如下图:
页面的跳转存在哪些问题呢
- 页面路径不好的管理问题
- 为什么不好管理:就是改页面路径,或者说分包的时候不用一个个找着改
- 传递参数的方式不太友好
- 为什么不友好:需要手动拼接参数列表
- 参数类型单一,只支持string
- 不管传string还是number和boolean得到的都是字符串
- 传特殊字符时会发现参数被截断(比如传二维码信息)
- 原因是不能包括(=&?)等特殊字符
创建一个项目
代码如下:
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>
此处仅作交流学习,版权归原作者所有。