uni-app 自定义路由封装模块详解(附源码逐行解读)

🚀uni-app 自定义路由封装模块详解(附源码逐行解读)

📌 请收藏 + 点赞 + 关注,获取更多 uni-app 项目实用技巧!

在实际 uni-app 项目中,我们常常需要对 uni.navigateTouni.switchTab 等 API 做一层封装,以便统一处理页面跳转、参数传递、登录拦截等逻辑。本篇将完整展示一份功能强大的路由封装方案,并逐行解释其实现逻辑,帮助你构建更可控、易扩展的项目架构。


📦源码展示(含说明性注释)

👇以下是完整源码,已集成:

  • 页面路径分析
  • query 和 params 分离
  • 登录拦截(导航守卫)
  • 页面跳转封装(支持多种跳转模式)
  • 首页识别与返回逻辑
  • 页签识别
  • 页面栈方法调用
  • 登录回调钩子

📄 完整代码如下(点击右侧小箭头可展开逐行解读):
点击展开完整源码逐行解读

ts 复制代码
// 引入 lodash 的 last 方法,返回数组最后一个元素
import { last } from "lodash-es";

// 引入 ctx 插件中编译期注入的页面配置、tabBar、subPackages 等
import { ctx } from "virtual:ctx";

// 项目中封装的 localStorage 工具
import { storage } from "../utils";

// 引入全局 config 配置
import { config } from "../../config";

type PushOptions = string | {
	path: string; // 路径
	mode?: "navigateTo" | "redirectTo" | "reLaunch" | "switchTab" | "preloadPage"; // 跳转方式
	events?: { [key: string]: (data: any) => void }; // 页面间事件通信
	query?: { [key: string]: any }; // URL 参数
	params?: { [key: string]: any }; // 缓存参数
	isGuard?: boolean; // 是否启用导航守卫
	[key: string]: any;
};

type Tabs = {
	text?: string;
	pagePath: string;
	iconPath?: string;
	selectedIconPath?: string;
	[key: string]: any;
}[];

// 获取所有页面配置
const routes = [...ctx.pages];

// 处理子包中的页面路径
if (ctx.subPackages) {
	ctx.subPackages.forEach((a) => {
		a.pages.forEach((b) => {
			routes.push({
				...b,
				path: a.root + "/" + b.path,
			});
		});
	});
}

// 注册钩子函数
const fn: { [key: string]: (...args: any[]) => any } = {};

// 路由核心对象
const router = {
	// 读取 tabBar 配置
	get tabs(): Tabs {
		if (ctx.tabBar) {
			return ctx.tabBar.list || [];
		} else {
			return [];
		}
	},

	// 全局样式
	globalStyle: ctx.globalStyle,

	// 所有路由
	routes,

	// 当前页面 URL query 参数
	get query() {
		const info = this.info();
		return { ...info?.query };
	},

	// 非 URL 参数,通过缓存传递
	get params() {
		return storage.get("router-params") || {};
	},

	// 页面路径配置
	get pages() {
		return {
			home: "/" + (ctx.tabBar ? this.tabs[0].pagePath : ctx.pages[0].path),
			...config.app.pages,
		};
	},

	// 当前页面信息对象
	currentPage() {
		return last(getCurrentPages())!;
	},

	// 当前路径
	get path() {
		return router.info()?.path;
	},

	// 当前页面完整信息
	info() {
		const page = last(getCurrentPages());

		if (page) {
			const { route, $page, $vm, $getAppWebview }: any = page;
			const q: any = {};

			// 解析 query 参数
			try {
				$page?.fullPath
					.split("?")[1]
					.split("&")
					.forEach((e: string) => {
						const [k, v] = e.split("=");
						q[k] = decodeURIComponent(v);
					});
			} catch (e) {}

			const style = this.routes.find((e) => e.path == route)?.style;

			return {
				$vm,
				$getAppWebview,
				path: `/${route}`,
				fullPath: $page?.fullPath,
				query: q || {},
				isTab: this.isTab(route),
				style,
				isCustomNavbar: style?.navigationStyle == "custom",
			};
		}

		return null;
	},

	// 页面跳转主函数
	push(options: PushOptions) {
		if (typeof options === "string") {
			options = { path: options, mode: "navigateTo" };
		}

		let {
			path,
			mode = "navigateTo",
			animationType,
			animationDuration,
			events,
			success,
			fail,
			complete,
			query,
			params,
			isGuard = true,
		} = options;

		// 拼接 query 到 URL
		if (query) {
			let arr = [];
			for (let i in query) {
				if (query[i] !== undefined) arr.push(`${i}=${query[i]}`);
			}
			path += "?" + arr.join("&");
		}

		// 缓存传参
		if (params) {
			storage.set("router-params", params);
		}

		const data = {
			url: path,
			animationType,
			animationDuration,
			events,
			success,
			fail,
			complete,
		};

		// 如果目标是 tab 页,强制使用 switchTab
		if (this.isTab(path)) {
			mode = "switchTab";
		}

		const next = () => {
			switch (mode) {
				case "navigateTo":
					uni.navigateTo(data); break;
				case "redirectTo":
					uni.redirectTo(data); break;
				case "reLaunch":
					uni.reLaunch(data); break;
				case "switchTab":
					uni.switchTab(data); break;
				case "preloadPage":
					uni.preloadPage(data); break;
			}
		};

		// 启用导航守卫
		if (fn.beforeEach && isGuard) {
			fn.beforeEach({ path: options.path, query }, next, (opt) => this.push(opt));
		} else {
			next();
		}
	},

	// 返回上一页或首页
	back(options?: UniApp.NavigateBackOptions) {
		if (this.isFirstPage()) {
			this.home();
		} else {
			uni.navigateBack(options || {});
		}
	},

	// 执行当前页面某个方法
	callMethod(name: string, data?: any) {
		const { $vm } = this.info()!;
		if ($vm && $vm.$.exposed?.[name]) {
			return $vm.$.exposed[name](data);
		}
	},

	// 是否第一页(判断是否需要返回首页)
	isFirstPage() {
		return getCurrentPages().length == 1;
	},

	// 是否是当前路径
	isCurrentPage(path: string) {
		return this.info()?.path === path;
	},

	// 返回首页
	home() {
		this.push(this.pages.home);
	},

	// 跳转 Tab 页
	switchTab(name: string) {
		const item = this.tabs.find((e) => e.pagePath.includes(name));
		if (item) {
			this.push({
				path: `/${item.pagePath}`,
				mode: "switchTab",
			});
		} else {
			console.error("Not found tab", name);
		}
	},

	// 是否是 Tab 页
	isTab(path: string) {
		return !!this.tabs.find((e) => path === `/${e.pagePath}`);
	},

	// 跳转登录页(支持 reLaunch)
	login(options?: { reLaunch: boolean }) {
		const { reLaunch = false } = options || {};
		this.push({
			path: this.pages.login,
			mode: reLaunch ? "reLaunch" : "navigateTo",
			isGuard: false,
		});
	},

	// 登录成功后的回调处理
	nextLogin(type?: string) {
		const pages = getCurrentPages();
		const index = pages.findIndex((e) => this.pages.login.includes(e.route!));

		if (index <= 0) {
			this.home();
		} else {
			this.back({ delta: pages.length - index });
		}

		storage.set("loginType", type);

		if (fn.afterLogin) fn.afterLogin();

		uni.$emit("afterLogin", { type });
	},

	// 注册路由钩子函数(beforeEach)
	beforeEach(callback: (to: any, next: () => void, reject: (opt: PushOptions) => void) => void) {
		fn.beforeEach = callback;
	},

	// 登录后执行回调
	afterLogin(callback: () => void) {
		fn.afterLogin = callback;
	},
};

export { router };

✍️ 核心功能说明(重点功能归纳)

功能模块 描述说明
router.push() 支持全模式页面跳转,封装 query/params,支持守卫
router.info() 获取当前页面详细信息
router.callMethod() 跨组件执行 exposed 方法
router.isTab() 判断路径是否为 Tab 页
router.beforeEach() 注册跳转拦截器
router.nextLogin() 登录回调重定向功能
router.pages 自动生成首页路径与配置路径

✅总结

该路由封装模块适用于 uni-app 项目中需要进行页面跳转逻辑统一管理的场景,具备:

  • 💡 统一跳转 API:支持 navigateTo、switchTab、reLaunch 等
  • 🔒 导航守卫机制:登录拦截与后置回调
  • 🔄 query/params 分离处理
  • 🧩 模块化配置,支持挂载 ctx

你可以在此基础上继续拓展如:权限校验、页面缓存、历史记录管理、动画过渡管理等功能。

相关推荐
huanmieyaoseng1003几秒前
Mybatis常见面试题
java·开发语言·mybatis
FmZero1 分钟前
后端全栈路线(9小时前端速成)
前端·vscode·学习
万世浮华戏骨3 分钟前
Web 后端 Python 基础安全
前端·python·安全
Dontla5 分钟前
JWT认证流程(JSON Web Token)
前端·数据库·json
無限進步D5 小时前
Java 运行原理
java·开发语言·入门
難釋懷5 小时前
安装Canal
java
是苏浙5 小时前
JDK17新增特性
java·开发语言
余人于RenYu6 小时前
Claude + Figma MCP
前端·ui·ai·figma
阿里加多8 小时前
第 4 章:Go 线程模型——GMP 深度解析
java·开发语言·后端·golang
杨艺韬8 小时前
vite内核解析-第2章 架构总览
前端·vite