uniapp封装路由管理(兼容Vue2和Vue3)
1:uniapp已经有路由管理了为什么还要二次封装路由?
- 简化配置和调用
- 增强灵活性和可扩展性
- 实现统一的功能和策略
- 提升开发效率和团队协作
2. 增强灵活性和可扩展性
- 灵活配置:二次封装允许开发者根据实际需求灵活配置路由参数,如跳转类型、页面参数等。这种灵活性使得路由管理更加符合项目的具体需求。
- 易于扩展:随着项目的不断发展,可能需要新增或修改路由配置。通过二次封装,可以方便地扩展路由功能,无需对原有代码进行大规模修改。
2. 集中管理,简化配置
- 统一配置:通过封装路由管理,将所有页面路由信息集中在一个地方进行配置和管理。这样不仅使得路由信息一目了然,还方便进行统一修改和维护。
- 简化跳转:开发者只需调用封装好的路由方法,如
$openPage
,而无需关心底层的路由实现细节,大大简化了页面跳转的代码。
3. 提高代码的可读性和可维护性
- 清晰的代码结构:通过封装,将路由处理逻辑与业务逻辑分离,使得代码结构更加清晰。开发者可以更容易地理解和维护项目的导航流程。
- 易于扩展:当需要新增或修改路由时,只需在配置中进行调整,无需修改业务代码,降低了代码耦合度,提高了可维护性。
4. 增强代码的复用性和灵活性
- 复用性:封装的路由方法可以在项目的不同部分重复使用,无需重复编写相同的跳转逻辑。
- 灵活性:通过配置参数,如跳转类型(
navigateTo
、switchTab
等),可以灵活地实现不同的跳转需求,满足不同场景下的导航要求。
5. 便于实现统一的功能和策略
- 统一处理:在路由封装中,可以方便地实现统一的路由拦截、权限校验等功能,确保所有页面跳转都遵循相同的规则。
- 全局参数传递:通过封装,可以更方便地在页面间传递参数,实现全局状态管理,提升用户体验。
6. 提升开发效率和用户体验
- 开发效率:封装的路由管理减少了重复代码的编写,提高了开发效率。开发者可以更快地实现页面跳转功能,专注于业务逻辑的开发。
- 用户体验:通过统一的路由管理,可以确保页面跳转的一致性和流畅性,提升用户在应用中的导航体验。
统一管理的优势
- 错误检查:在处理路由配置时,添加了错误检查机制,确保每个页面都有必要的属性(如
name
),从而避免运行时错误。 - 默认值处理:在不需要指定跳转类型时,提供默认的
navigateTo
,简化了配置。 - 支持SubPackages:通过处理
subPackages
,支持分包加载,优化了应用的性能和加载时间。
我这里简单做了一下封装有什么问题欢迎留言指正
在项目中创建min.router.config.js文件
/**
* min.router.config.js
* 使用方法
* Vue2
* 示例:this.$openPage({name: 'my', query: {id: 123}, type: 'navigateTo'}) 传参方式
* 示例:this.$openPage('my') 不传参数可以简写
* Vue3 setup语法糖
* 示例:
* import { getCurrentInstance } from 'vue';
* const { $openPage } = getCurrentInstance().appContext.config.globalProperties;
* $openPage({name: 'my', query: {id: 123}, type: 'navigateTo'}) 传参方式
* $openPage('my') 不传参数可以简写
* Vue2 和 Vue3
* 视图中可直接使用 $openPage('my')
* @param name 字符串地址名
* @param query 传递的参数
* @param type 跳转的方式 ['navigateTo', 'switchTab', 'reLaunch', 'redirectTo'] 不传默认navigateTo
* this.$parseURL() 解析路由参数
* 使用方法在接收参数页this.$parseURL(options)
*/
// #ifdef VUE3
import { getCurrentInstance } from 'vue';
// #endif
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, type, 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.$routerConfig.forEach(item => {
if (item.name === name) {
path = item.path;
type = 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() {
// #ifdef VUE2
const query = this.$root.$mp.query.query;
// #endif
// #ifdef VUE3
const query = getCurrentInstance().proxy.$root.$scope.options.query;
// #endif
if (query) {
return JSON.parse(decodeURIComponent(query))
} else {
return {}
}
}
function install(Vue, routerConfig) {
if (!routerConfig || !Array.isArray(routerConfig.options.routes)) {
throw new Error("routerConfig 必须是一个带有 routes 属性的对象");
}
// Vue 2 和 Vue 3 的兼容混入
Vue.mixin({
beforeCreate: function() {
if (!isDefault(routerConfig)) {
Vue._routerConfig = routerConfig;
}
}
})
// 定义全局属性
const globalProperties =
// #ifdef VUE2
Vue.prototype
// #endif
// #ifdef VUE3
Vue.config.globalProperties
// #endif
;
Object.defineProperty(globalProperties, '$routerConfig', {
get: function() {
return Vue._routerConfig._router;
}
})
Object.defineProperty(globalProperties, '$parseURL', {
get: function() {
return parseURL.bind(this);
}
})
Object.defineProperty(globalProperties, '$openPage', {
get: function() {
return openPage.bind(this);
}
})
}
function minRouterConfig(options) {
if (!(this instanceof minRouterConfig)) {
throw Error("minRouterConfig是一个构造函数,应该用`new`关键字调用")
}
isDefault(options) && (options = {})
this.options = options;
this._router = options.routes || [];
}
minRouterConfig.install = install;
minRouterConfig.prototype.openPage = openPage;
minRouterConfig.prototype.parseURL = parseURL;
export default minRouterConfig;
创建router.config.js文件
/*
* 使用方法
* Vue2
* 示例:this.$openPage({name: 'my', query: {id: 123}, type: 'navigateTo'}) 传参方式
* 示例:this.$openPage('my') 不传参数可以简写
* 示例:<view @click="$openPage('ceshi')"></view> 奈何小程序不支持这样跳转
* Vue3 setup语法糖
* 示例:
* import { getCurrentInstance } from 'vue';
* const { $openPage } = getCurrentInstance().appContext.config.globalProperties;
* $openPage({name: 'my', query: {id: 123}, type: 'navigateTo'}) 传参方式
* $openPage('my') 不传参数可以简写
* Vue2 和 Vue3
* 视图中可直接使用 $openPage('my')
* 配置参数项说明:
* name:可选配置 (路由名称)
* path:必填配置 (路由地址)
* type 跳转的方式 ['navigateTo', 'switchTab', 'reLaunch', 'redirectTo'] 不传默认navigateTo
*/
import pagesJson from '@/pages.json'
import minRouterConfig from './min.router.config.js'
// 处理 pages 数组
function processPages(pages) {
return pages.map(page => {
if (!page.name) {
throw Error(`Page is missing the 'name' property: ${JSON.stringify(page)}`);
}
const processedPage = {
name: page.name,
path: page.path
}
// 仅在 type 不存在时不添加默认值
if(page.type) {
processedPage.type = page.type;
}
return processedPage
});
}
// 处理 subPackages 数组
function processSubPackages(subPackages) {
if(!subPackages) return []
let routes = [];
subPackages.forEach(subPackage => {
routes = routes.concat(processPages(subPackage.pages).map(page => {
return {
...page,
path: subPackage.root + '/' + page.path
};
}));
});
return routes;
}
const router = new minRouterConfig({
routes: [ // 权限路由 在main.js可实现路由拦截 所以路由都需要注册
...processPages(pagesJson.pages),
...processSubPackages(pagesJson.subPackages)
]
})
export default router
router.config.js文件无需配置任何路径,均在pages.json中配置即可
最后在main.js中我们全局挂载一下
import App from './App'
// 引入全局方法
import minRouterConfig from '@/config/min.router.config.js'
import routerConfig from '@/config/router.config.js'
// #ifndef VUE3
import Vue from 'vue'
import './uni.promisify.adaptor'
Vue.config.productionTip = false
App.mpType = 'app'
const app = new Vue({
...App
})
app.$mount()
// #endif
// #ifdef VUE3
import { createSSRApp } from 'vue'
export function createApp() {
const app = createSSRApp(App)
app.use(minRouterConfig, routerConfig)
return {
app
}
}
// #endif
我这里是在项目根目录创建了一个config文件夹,两个文件都在config文件夹中
以上配置在uniapp框架下不管是Vue2或者是Vue3都通用
不管是Vue2的组合式API下还是Vue3的setup语法糖下都可以正常使用
在页面中使用
Vue3中 setup语法糖
<template>
<button @click="onClick">跳转我的</button>
</template>
<script setup>
import { getCurrentInstance } from 'vue';
// data数据
const { $openPage } = getCurrentInstance().appContext.config.globalProperties;
// methods方法
const onClick = () => {
$openPage('user')
}
</script>
<style>
</style>
Vue2 组合式API
<template>
<button @click="onClick">跳转我的</button>
</template>
<script>
export default {
methods: {
onClick() {
this.$openPage('user')
}
}
}
</script>
<style>
</style>
通用
<template>
<button @click="$openPage('user')">跳转我的</button>
</template>
<script>
</script>
<style>
</style>
以上有什么问题可以下面留言一起学习