更新记录
1.0.1(2026-07-10) 下载此版本
更新日志
v1.0.0 (2026-07-10)
- 首发:声明式路由中间件 + RBAC 权限引擎
- 内置
auth中间件(未登录自动跳登录页并携带?redirect=回跳) - 导航包装 API:
router.go / replace / relaunch / tab / back / toHome / toLogin / toFail canAccess复杂权限(与页面roles做 AND)、action:指令、节流、afterEach后置守卫- 函数式适配器
navigateTo / redirectTo / reLaunch / switchTab / navigateBack - 纯 JS/TS、零三方运行时依赖,H5 / 小程序 / App 多端通用
平台兼容性
uni-app(3.99)
| Vue2 | Vue3 | Chrome | Safari | app-vue | app-nvue | Android | iOS | 鸿蒙 |
|---|---|---|---|---|---|---|---|---|
| √ | √ | √ | √ | √ | √ | √ | √ | √ |
| 微信小程序 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 小红书小程序 | 快应用-华为 | 快应用-联盟 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| √ | √ | √ | √ | √ | √ | √ | √ | √ | √ | √ | √ |
ailingyun-router-guard(基于 uniapp-pages-router 的声明式路由中间件 + RBAC)
基于 uniapp-pages-router 的声明式路由中间件 + RBAC 引擎。
一切皆中间件:登录校验、企业认证、VIP 校验、埋点……同一种 (ctx, next) 写法,可同步可异步。
本插件是通用、可移植的,不依赖任何具体项目的 store / 常量 / UI;用到状态的地方都用「你的 store」抽象表示,复制到其他工程直接套用即可。
一句话原则:每次跳转都先过一遍中间件管线(全局 → 页面 → RBAC 闸门),通过才真正调用
uni导航。
一、插件简介
- 声明式守卫:在
pages.json里用middleware/roles声明页面级权限,不在业务代码里写一堆if (!login) uni.redirectTo(...)。 - 中间件管线:「全局中间件 → 页面中间件 → RBAC 自动闸门」统一契约
(ctx, next),同步异步皆可,可redirect/abort。 - 内置
auth:页面加"middleware": ["auth"]即生效,未登录自动跳登录页并携带?redirect=回跳地址。 - RBAC +
canAccess:roles命中其一即放行;复杂权限用canAccess(path, roles)做同步细粒度判断(与roles做 AND)。 - 导航包装:
router.go / replace / relaunch / tab / back / toHome / toLogin / toFail,兼容原生success/fail/animationType等透传。 - 纯 JS/TS:无三方运行时依赖,H5 / 小程序 / App 多端通用。
二、版本信息
| 项 | 内容 |
|---|---|
| 当前版本 | 1.0.0 |
| 更新日期 | 2026-07-10 |
| 兼容环境 | uni-app(Vue2 / Vue3 均可),HBuilderX 3.1.0+ |
| 体积 | 轻量纯 JS/TS,无三方运行时依赖 |
| 前置依赖 | uniapp-pages-router(开发依赖,>=1.2.0) |
三、安装与引入
1. 前置依赖(uniapp-pages-router,开发依赖)
本插件依赖 uniapp-pages-router 在编译期把路由表注入为全局常量,并提供 uniRouter() Vite 插件。它是编译期 / 构建期依赖(只参与打包,不进运行时 bundle),按开发依赖安装:
npm install -D uniapp-pages-router # 或 pnpm add -D / yarn add -D
它负责两件事:
- 编译期生成全局常量
UNI_ROUTE_MAP/UNI_ROUTES/UNI_TABBAR_ROUTES/UNI_TABBAR_MAP; - 提供
uniRouter({ pages, fields })插件,把middleware/roles等自定义字段注入路由表。
插件只在运行时读取这些常量。不安装 / 不配置它也能用:此时
UNI_ROUTE_MAP为空,所有页当公开页,引擎退化为纯导航层(路径未知会console.error)。
2. 导入本插件(市场导入 / 手动导入)
- 市场导入:在 HBuilderX 插件市场点击「使用 HBuilderX 导入插件」,会自动放到
js_sdk/aly-router-guard/。 - 手动导入:把本目录整体放到项目根目录的
js_sdk/aly-router-guard/下即可,可改目录。
3. 编译期注入守卫字段(vite.config.ts)
让 uniRouter 把 middleware / roles 注入路由表:
import { uniRouter } from "uniapp-pages-router";
uniRouter({
pages: "src/pages.json",
fields: ["middleware", "roles"], // 关键:声明要注入的自定义字段
});
不配核心包也能用——此时所有页当公开页,引擎退化为纯导航层,路径未知会
console.error提示。
4. 引入方式
import {
initRouter,
overrideUni,
router,
navigateTo,
redirectTo,
reLaunch,
switchTab,
navigateBack,
} from "@/js_sdk/aly-router-guard/index.js";
目录结构
js_sdk/aly-router-guard/
├── index.js ← 装配入口:initRouter / overrideUni / router / 导航函数
├── index.d.ts ← TS 类型入口
├── core/ ← 引擎核心(纯逻辑,无导航/UI 依赖)
│ ├── config.js ← 配置单例(模块间唯一可变状态)
│ └── runner.js ← 守卫管线 runOne / runPipeline
├── middlewares/ ← 中间件
│ ├── auth.js ← 内置 auth 中间件
│ └── index.js ← 内置中间件注册表 builtinMiddlewares
├── navigator/ ← 导航层
│ ├── index.js ← Router 类 + 捕获原始 uni + 适配器
│ └── utils.js ← parseTarget / buildQuery / currentPagePath
├── types/ ← TS 类型(JS 项目也可受益于 JSDoc)
├── examples/ ← 非运行时示例(middlewares.example.js)
├── package.json
└── README.md
目录名
ailingyun-router-guard可按你的项目规划改名或移动到其他位置(详见上方说明),导入路径相应调整即可。移到其他目录也无影响。
四、快速上手
1. 30 秒上手(最小可用)
// 1) 初始化(App 启动一次)
initRouter({
isLogin: () => !!userStore.token,
loginUrl: '/pages/member/login/login',
homeUrl: '/pages/index/index',
})
// 2) 保护页面(pages.json)
{ "path": "pages/profile/profile", "middleware": ["auth"] }
// 3) 跳转(任意页面)
import { router } from '@/js_sdk/aly-router-guard/index.js'
router.go('/pages/profile/profile')
就这三步:初始化 → 在 pages.json 声明守卫 → 用 router.go 跳转。
2. 在 App 启动时初始化一次(initRouter)
import {
initRouter,
overrideUni,
router,
} from "@/js_sdk/aly-router-guard/index.js";
import { useUserStore } from "你的store路径";
const userStore = useUserStore();
initRouter({
// 登录态 & 角色(RBAC 用,可异步)
isLogin: () => !!userStore.token,
getRoles: () => userStore.roles || [],
// 系统页地址(按你的工程填)
loginUrl: "/pages/member/login/login",
homeUrl: "/pages/index/index",
failUrl: "/pages/public/fail",
fallback: "/pages/public/forbidden", // RBAC 无权限时的回退页(优先于 loginUrl)
webviewUrl: "/pages/public/web", // 非 H5 打开外链用的 webview 页
// 未登录自定义处理(配了就不强制跳登录页,自行决定 toast / 弹窗 / 跳转)
onAuthFail: (ctx) => {
uni.showToast({ title: "请先登录", icon: "none" });
},
// 后置守卫
afterEach: (ctx) => console.log("跳转完成:", ctx.path, "<-", ctx.from),
// 你的业务中间件注册表(key 可覆盖内置 auth)
middlewares: {
companyCheck: (ctx, next) => {
if (userStore.companyVerified) return next();
ctx.redirect("/pages/company/apply");
},
},
// action: 指令型处理器(不导航,只触发回调)
actions: {
share: () => uni.showToast({ title: "分享" }),
},
});
// 让三方库 / 隐式 uni.navigateTo 也走守卫
overrideUni();
// 可选:挂到全局方便任意地方调用
uni.$router = router;
3. 声明式守卫(pages.json)
无需在页面代码里写 if (!login) uni.redirectTo(...),直接在路由表里声明:
{
"pages": [
{ "path": "pages/index/index" }, // 公开页
{ "path": "pages/profile/profile", "middleware": ["auth"] }, // 需登录(内置 auth)
{
"path": "pages/admin/dashboard",
"middleware": ["auth"],
"roles": ["admin"],
}, // 登录 + admin
{
"path": "pages/company/home",
"middleware": ["auth", "companyCheck"],
"roles": ["company"],
},
],
}
middleware: [...]→ 页面级中间件,按数组顺序执行。roles: [...]→ RBAC 自动闸门:用户角色命中其一即放行。
4. 导航 API(带示例)
import { router } from "@/js_sdk/aly-router-guard/index.js";
router.go("/pages/profile/profile"); // 普通跳转(自动选 navigateTo / switchTab)
router.go("/pages/profile/profile", { id: 1 }); // 带参数对象,自动拼成 query
router.go("UserCenter"); // 用别名(pages.json 的 alias)
router.replace("/pages/main/mine"); // 替换当前页(redirectTo)
router.relaunch("/pages/main/home"); // 重启(reLaunch)
router.tab("/pages/main/home"); // 跳 tabBar(switchTab)
router.back(); // 返回上一页
router.toHome(); // 回首页
router.toLogin(); // 去登录页,自动携带 ?redirect=当前页
router.toFail(404, "页面不存在"); // 去错误页
函数式适配器(直接替代 uni.*):
import {
navigateTo,
redirectTo,
reLaunch,
switchTab,
navigateBack,
} from "@/js_sdk/aly-router-guard/index.js";
navigateTo("/pages/x/x"); // → router.go
redirectTo("/pages/x/x"); // → router.go(..., { redirect: true })
reLaunch("/pages/x/x"); // → router.go(..., { reLaunch: true })
switchTab("/pages/x/x"); // → router.go
navigateBack(1); // 数字 delta,或 { delta: 1 }
success/fail/animationType等原生选项会原样透传给uni,不会被吞。
target 形式:
| 形式 | 行为 |
|---|---|
/pages/x/x 或 /pages/x/x?id=1 |
普通路径,query 与 params 合并 |
| 别名 | 命中 alias 字段,归一到真实 path |
http(s)://... |
H5 直接打开;非 H5 跳 webviewUrl?url=<编码外链> |
action:xxx |
触发 config.actions['xxx'](),不导航 |
go 的 options 字段:
go / replace / relaunch 的第三个参数 options 会透传给底层 uni,并支持以下引擎保留字段:
| 字段 | 作用 |
|---|---|
skipGuard: true |
跳过整个守卫管线,直接跳转(登录回跳等强制跳转用) |
redirect: true |
用 redirectTo(也可直接调 router.replace) |
replace: true |
同 redirect: true,用于登录类重定向(替换当前页) |
reLaunch: true |
用 reLaunch(也可直接调 router.relaunch) |
onError: (e) => void |
跳转失败回调(同时会 console.error) |
其它(success / fail / animationType / events …) |
原样透传给 uni |
五、高级用法
1. 中间件系统
契约:
type MiddlewareFn = (
ctx: MiddlewareCtx,
next: () => void,
) => void | Promise<void>;
写一个中间件,本质上就是决定下面四种行为之一。以下只是示例模板,不是库内置函数(库目前只内置 auth 一个中间件,见下方「内置 auth 中间件」);把它们复制到你的 middlewares.js 改名即可用:
// 1) 放行(调用 next 继续管线)
export function pass(ctx, next) {
next();
}
// 2) 重定向(默认 navigateTo 入栈,保留来源页,使「返回上一页」可用)
export function goLogin(ctx) {
ctx.redirect("/pages/member/login/login");
}
// 3) 重定向且替换当前页(登录类重定向常用)
export function goLoginReplace(ctx) {
ctx.redirect("/pages/member/login/login", { replace: true });
}
// 4) 静默中止(不跳转,停留在当前页)
export function silent(ctx) {
ctx.abort();
} // 或不调 next 直接 return
你真实项目里的中间件长这样(参考库内 examples/middlewares.example.js):
export const vipOnly = async (ctx, next) => {
if (useUserStore().roles.includes("vip")) return next(); // 行为 1:放行
ctx.redirect("/pages/public/forbidden"); // 行为 2:重定向
};
ctx.redirect(target, opts?) 行为(重要)
- 默认
navigateTo入栈,保留来源页,使「返回上一页」可用; - 传
{ replace: true }→redirectTo替换当前页; - 传
{ reLaunch: true }→reLaunch重启。 - 内置
auth未登录重定向即使用{ replace: true };RBAC 无权限回退默认入栈。
ctx 字段:path 目标 · route 路由记录 · from 来源页 · params 参数 · roles 当前页角色 · redirect · abort。
2. 内置 auth 中间件
builtinMiddlewares.auth 已内置,页面加 "middleware": ["auth"] 即生效:
isLogin()为真 → 放行;- 为假且配了
onAuthFail→ 调用它(自行决定弹窗/跳转),不强制跳登录页; - 否则重定向
loginUrl?redirect=<原目标>,登录页onLoad(query.redirect)可回跳。
3. 自定义中间件
// middlewares.js(放在你的业务层)
import { useUserStore } from "你的store路径";
export const vipOnly = async (ctx, next) => {
const roles = useUserStore().roles || [];
if (roles.includes("vip")) return next();
ctx.redirect("/pages/public/forbidden"); // 无权限 → 403 页(默认入栈)
};
注册方式(二选一):
- 声明式(按名启用):在
initRouter({ middlewares: { vipOnly } })注册,pages.json用"middleware": ["vipOnly"]引用。 - 运行时全局:
router.use(vipOnly)对所有页面生效。
4. 执行顺序
全局中间件 → 页面声明式中间件 → RBAC 自动闸门,全部通过才真正跳转。
- 任一环调用
ctx.redirect(t)会立即终止管线并跳转到t(后续中间件 / RBAC 不再执行); - 调用
ctx.abort()或不调next直接return→ 静默中止,停留在当前页; - 未注册的
middlewarekey 会console.warn并跳过,不会崩溃。
5. RBAC 自动闸门与 canAccess
给页面声明 roles,引擎自动校验,无需写中间件:
{ "path": "pages/admin/dashboard", "roles": ["admin"] }
getRoles()返回当前用户角色;命中其一即放行(some)。- 都不满足 → 跳
fallback(优先)或loginUrl;两者都没配则静默中止。
canAccess 复杂权限:
当「角色命中仍不够、还要按路径 / 资源做更细判断」时,用 canAccess:
initRouter({
getRoles: () => userStore.roles || [],
canAccess: (path, userRoles) => boolean,
});
它与页面 roles 的关系(AND 运算):
最终放行 = (页面 roles 命中其一) AND (canAccess(path, 用户角色) === true)
调用时机与范围(关键):
- 只要配置了
canAccess,它会对每一次跳转都执行(不论该页是否声明roles); - 页面声明了
roles→ 先校验角色命中,再与canAccess结果做 AND; - 页面没声明
roles→ 角色部分视为通过,最终是否放行完全由canAccess决定(相当于用canAccess做整站权限矩阵)。
⚠️
canAccess是同步调用(不被 await):必须直接return boolean;需要异步(如调接口拉权限)时改用自定义中间件。 ⚠️canAccess只拿到(path, 用户角色),拿不到本次跳转的params;要基于参数判断权限请用自定义中间件(可访问ctx.params)。
示例 1:基于路径的权限码矩阵
const permissionMap = {
"/pages/admin/dashboard": ["admin.dashboard"],
"/pages/admin/users": ["admin.users"],
"/pages/finance/bill": ["finance.bill.read"],
};
initRouter({
getRoles: () => userStore.roles || [],
canAccess: (path, roles) => {
const required = permissionMap[path];
if (!required) return true; // 不在矩阵里的页,按 roles / 公开 处理
const owned = userStore.permissions || []; // 你从接口/菜单拿到的权限码
return required.every((p) => owned.includes(p)); // 所需权限码全拥有才放行
},
});
示例 2:按路径前缀做整站级限制
initRouter({
getRoles: () => userStore.roles || [],
canAccess: (path) => {
// canAccess 每次都跑:即使页面没声明 roles,也能在这里统一卡后台页
if (path.startsWith("/pages/admin/"))
return userStore.roles.includes("admin");
return true;
},
});
示例 3:整站「维护模式」一刀切
initRouter({
canAccess: (path) => {
if (path === "/pages/public/maintain") return true; // 维护页本身永远可进
return !appStore.maintaining; // 维护期间其余页全部拦截
},
});
与自定义中间件的取舍:
- 简单「角色命中即放行」→ 页面
roles; - 按路径 / 资源做同步细粒度判断 →
canAccess; - 需要异步接口、或要
ctx.redirect到特定页、或要埋点日志 → 自定义中间件(router.use或middlewares注册)。
6. 其它高级
skipGuard(强制跳转,常用于登录回跳)
// 登录成功后强制回跳,跳过守卫避免被再次拦截
router.go(decodeURIComponent(query.redirect), {}, { skipGuard: true });
action: 指令(不导航,仅触发回调)
action: 是一种「伪跳转」:它复用 router.go 的统一入口,但目标以 action: 开头时不导航,而是执行你在 initRouter 里注册的同名回调函数,并立即返回、不走任何守卫管线。
① 注册处理器
initRouter({
// ...
actions: {
share: () => uni.showToast({ title: "分享" }),
logout: () => {
userStore.logout() // 你的登出逻辑(读 store / 清 token 等)
router.toLogin() // 处理器内部可自行再调用 router 导航
},
contact: () => uni.makePhoneCall({ phoneNumber: "400-xxx" }),
},
})
② 触发
router.go("action:share") // 触发 actions.share(),不跳转
router.go("action:logout") // 触发 actions.logout()
在模板里也可以直接绑:
<view @click="router.go('action:share')">分享</view>
<view @click="router.go('action:logout')">退出登录</view>
③ 关键行为(务必了解)
- 不导航、不进守卫:
action:在解析阶段就直接返回,因此不会经过全局中间件 / 页面中间件 / RBAC 闸门,也不会触发afterEach。它本质上是「页面内的一条本地指令」,不是路由。 - 处理器内部可再导航:处理器里是你自己的代码,可自由再调用
router.toLogin()/router.go(...);此时新的跳转会正常走守卫管线。 - 未知 action 静默忽略:若
config.actions里没有对应键(或根本没配actions),router.go("action:xxx")什么都不做、也不报错——适合「指令名可能动态来自后端」的场景。 - 不解析参数:
action:只携带指令名,不解析 query,处理器拿不到任何入参;需要数据请在闭包里捕获,或读全局状态 / store。 - 统一入口的价值:因为和真实路径共用
router.go,你可以把「跳转」与「本地指令」混在同一个数据驱动列表里,用同一个处理函数,无需区分分支:
const items = [
{ label: "个人资料", target: "/pages/member/profile" },
{ label: "分享", target: "action:share" },
{ label: "退出登录", target: "action:logout" },
]
// 统一处理:onTap(item) { router.go(item.target) }
节流(防连点)
router.setThrottleDelay(0); // 关闭;默认 300ms,只对“短时间重复点同一目标”生效
全局中间件管理
router.use(mw); // 注册全局中间件
router.clearMiddlewares(); // 清空所有运行时注册的全局中间件(调试用)
7. 配置项一览(initRouter(cfg))
| 字段 | 类型 | 说明 |
|---|---|---|
middlewares |
Record<string, fn> |
命名中间件注册表,key 可覆盖内置 auth |
getRoles |
() => string[] \| Promise<string[]> |
当前用户角色(供 RBAC / canAccess) |
isLogin |
() => boolean \| Promise<boolean> |
登录态(内置 auth 用,可异步校验 token) |
loginUrl |
string |
登录页;auth 未登录重定向目标;toLogin() 默认目标 |
homeUrl |
string |
首页;toHome() 目标 |
failUrl |
string |
错误页;toFail(code, msg) 目标 |
fallback |
string |
RBAC 无权限重定向目标(优先于 loginUrl) |
onAuthFail |
(ctx) => void |
未登录自定义处理,配了就不强制跳登录页 |
afterEach |
(ctx) => void |
后置守卫,每次成功跳转后回调 |
canAccess |
(path, roles) => boolean |
复杂权限 AND 兜底;同步调用、对每次跳转都执行,与 roles 命中做「与」运算(详见本节 5) |
webviewUrl |
string |
非 H5 打开外链的 webview 承载页 |
actions |
Record<string, () => void> |
action:xxx 指令处理器:不导航、不走守卫,执行同名回调;未知键静默忽略 |
throttleDelay |
number |
节流阈值 ms,默认 300 |
globalMiddlewares |
fn[] |
全局中间件,等价于逐个 router.use() |
8. API 速查
| 方法 | 说明 |
|---|---|
go(target, params?, options?) |
通用跳转,自动选 navigateTo / switchTab |
replace / relaunch / tab |
redirectTo / reLaunch / switchTab |
back(delta = 1) |
返回(H5 走 history.go) |
toHome() / toLogin() / toFail(code, msg) |
系统页快捷跳转 |
use(mw) / clearMiddlewares() |
全局中间件注册 / 清空 |
setThrottleDelay(ms) |
调节流阈值 |
getRoute(path) / isTabBar(path) |
路由查询 |
hasHistory() / canGoBack(delta) |
历史 / 可否返回 |
navigateTo / redirectTo / reLaunch / switchTab / navigateBack |
函数式适配器 |
六、示例项目
目录结构见第三章。examples/middlewares.example.js 提供了完整可参考的中间件写法,覆盖:登录校验、企业认证、VIP 专属、异步 token 校验、全局埋点,以及它们与 initRouter 注册、pages.json 声明的对应关系(三处一一对应)。复制时按文件内注释的「① 定义 / ② 注册 / ③ 声明」落位即可。
// 见 js_sdk/aly-router-guard/examples/middlewares.example.js
// 关键片段:
export const vipOnly = (ctx, next) => {
if (getIsVip()) return next();
ctx.abort(); // 静默中止:停留在当前页,不跳转
};
七、更新日志
v1.0.0 (2026-07-10)
- 首发:声明式中间件 + RBAC 引擎
- 内置
auth中间件(未登录跳登录页并回跳) - 导航包装 API:
router.go / replace / relaunch / tab / back / toHome / toLogin / toFail canAccess复杂权限(与roles做 AND)、action:指令、节流、afterEach后置守卫- 函数式适配器
navigateTo / redirectTo / reLaunch / switchTab / navigateBack
八、常见问题(FAQ)
- 系统页不要加
auth/roles(登录页、失败页等),否则可能自我重定向;确需进入用{ skipGuard: true }。 <navigator>组件拦不到:本引擎与uni.addInterceptor都拦不住原生组件跳转,请统一用router.go/navigateTo。- tabBar 页不接收参数:
uni.switchTab平台限制,tab/switchTab带参目标页也拿不到,需传参请用全局状态或uni.$emit。 overrideUni()只改写 4 个 API:navigateTo/redirectTo/reLaunch/switchTab;navigateBack未改写。action:xxx不导航:仅触发回调,适合「分享 / 退出登录 / 客服」等指令。- 对象 / 数组参数自动 JSON 序列化进 query,接收端按需
JSON.parse。
九、贡献与反馈
欢迎在插件市场该页面下提评论 / 反馈,或提交 Issue / PR。
插件 ID:ailingyun-router-guard。
十、许可证
隐私与权限声明
- 本插件需要申请的系统权限列表:无
- 本插件采集的数据、发送的服务器地址、以及数据用途说明:插件不采集任何数据
- 本插件是否包含广告:无

收藏人数:
下载插件并导入HBuilderX
下载插件ZIP
赞赏(0)
下载 210
赞赏 0
下载 12414839
赞赏 1933
赞赏
京公网安备:11010802035340号