uni-app提供了路由功能來實現頁面跳轉,可是在使用的過程當中咱們仍是發現有些不方便的地方,以下圖:vue
建立一個項目git
代碼以下github
export default {
// 首頁頁面
index: '/pages/index/index.vue',
// 個人頁面
my: '/pages/my/index.vue'
}複製代碼
使用的時候變成這樣json
<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特別麻煩,先不解決這個問題小程序
下面來看第二和第三個問題bash
先看個例子
app
參數比較多的狀況下這樣確實很差操做麻煩,能不能把參數部分單獨拿出來而後拼接到url上面呢?
函數
驚奇的發現傳過來的數字居然變成字符串了,參數不能保真ui
第四個問題就不演示了this
廢話很少說了,說了這麼多,相信大家也應該知道問題的所在了,下面就來解決這些問題 (漸進式講解)
首先建立一個文件(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
答案是確定的
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 {}
}
}複製代碼
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>複製代碼
能不能和RouterLink同樣使用
<router-link to="{name: 'my'}"></router-link>
<router-link to="my"></router-link>
<router-link to={name: 'my', query: {id: 123}}></router-link>複製代碼
能夠可是小程序不支持函數式組件,也就是不能一塊兒封裝到MinRouter
須要本身實現router-link
<template>
<div @click="openPage">
<slot></slot>
</div>
</template>
<script>
export default {
props: {
to: {
type: [Object, String],
required: true
}
},
methods: {
openPage () {
this.$openPage(this.to)
}
}
}
</script>複製代碼
在main.js文件添加
import mina from './components/min-a.vue'
Vue.component('min-a', mina)複製代碼
使用方式
<template>
<view class="content">
<image class="logo" src="/static/logo.png"></image>
<view>
<text class="title">{{title}}</text>
</view>
<button @click="toPage">跳轉到個人頁面</button>
<min-a to="my">跳轉到個人頁面min-a標籤跳轉</min-a>
<min-a :to="{name: 'my', query: {id: 123}}">跳轉到個人頁面min-a標籤跳轉帶參數</min-a>
</view>
</template>複製代碼
到此完結
補充:全局的beforeEach鉤子函數,這是後面添加的上面MinRouter代碼中不全,具體操做看github
注意:判斷條件必定要寫好,要否則會形成棧溢出