新增 有家相册 uni-app 前端(uniapp/youjiala,H5 / 微信小程序 / App 三端)
从旧 WebVue 移植的地基部分: - 登录页(RSA+MD5 密码加密、redirect 回跳、中英文切换)与登录后落地页 - 401 / 403 / 404 错误页 + error-panel 通用外壳(文案走 ErrorPage 语言包) - 登录守卫双链路:uni.addInterceptor 拦站内跳转,页面级 onShow 复核冷启动深链 - 单一请求链路 common/http.interceptor:JWT 唯一来源、401 并发刷新单例、 业务异常识别、HTTP 403 跳无权限页 - H5 干净短地址:initRouter 前向 __uniRoutes 追加克隆记录(router/h5-clean-url) - 路由登记表 router/routes.ts(PAGE_PATHS / pageUrl / PAGE_PERMISSIONS) 与新增页面四步登记约定(src/md/page-routing.md) - i18n:uView Pro 语言包 + 后端词条合并,双取值(后端 zh-cn/en,包名 zh-CN/en-US) - 后端接口层 service/ 与数据模型 data/auto/ 为生成器产物,不手改 不入库的内容: - 摘除脚手架残留的 Harmony 签名密码(manifest.json signingConfigs 与 harmony-configs/build-profile.json5 的 keyPassword / storePassword 已置空) - 忽略 node_modules、dist、unpackage、.hbuilderx、.agents、pnpm-lock.yaml 及根目录临时截图
This commit is contained in:
1 parent
74757f9eb4
commit
6a8258ac4b
274 files changed
+30291
No files matched your search
@@ -0,0 +1,301 @@
|
||||
# 页面路由与短地址规范
|
||||
|
||||
适用范围:`src/pages.json`、`src/manifest.json`、`src/router/routes.ts`、`src/router/h5-clean-url.ts`、`src/main.ts`。
|
||||
本文记录"为什么这么设计"和"新增页面要动哪几处",代码里的注释只解释局部行为,跨文件的约束以本文为准。
|
||||
|
||||
---
|
||||
|
||||
## 1. 目标:H5 地址形态
|
||||
|
||||
需求是**对齐旧 WebVue 系统的地址风格**(`https://uat.youjiala.com/dashboard`),同时保住 uni-app 的 `pages/` 目录结构。
|
||||
|
||||
| 阶段 | 地址 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| 旧 WebVue | `/login`、`/dashboard` | vue-router 4 + `createWebHistory`,可自由定义 path |
|
||||
| 本项目改造前 | `/#/pages/login/login` | hash 模式 + `pages.json` 的 path 字面即 URL |
|
||||
| 中间态(被否决) | `/login`,但文件在 `src/login.vue` | 页面文件被迫上移到 `src` 根,放弃 `pages/` 约定 |
|
||||
| 本项目现状 | `/login`、`/dashboard`,文件仍在 `pages/xxx/xxx.vue` | history 模式 + 运行时追加短地址路由记录 |
|
||||
|
||||
三端最终形态:
|
||||
|
||||
```
|
||||
src/pages/login/login.vue pages.json: pages/login/login URL / 和 /login
|
||||
src/pages/dashboard/dashboard.vue pages.json: pages/dashboard/dashboard URL /dashboard
|
||||
小程序 / App 端:只认真实路径 pages/login/login、pages/dashboard/dashboard
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. uni-app 的路由模型(与 vue-router 的本质差异)
|
||||
|
||||
- **路由表编译期静态注册**:`pages.json` 的 `pages` 数组是唯一来源,没有 `router.addRoute`,没有嵌套路由,没有全局 `beforeEach`。
|
||||
- **URL 由 path 字面决定**:uni-h5-vite 的 `generatePageRoute` 生成
|
||||
|
||||
```text
|
||||
// uni-h5-vite/dist/pagesJson.js 逐字摘录(引号为原样,非项目代码)
|
||||
const alias = isEntry ? `\n alias:'/${path}',` : '';
|
||||
return `{ path:'/${isEntry ? '' : path}',${alias} ...`
|
||||
```
|
||||
|
||||
入口页被特殊映射为 `/`(真实路径记在 `alias` 与 `meta.route`),**其余页面的 URL 就是 `pages.json` 里 `path` 的逐字拷贝**。除入口页外,uni-app 没有任何"URL 重命名"配置项——这是整篇文档所有绕路的根源。
|
||||
- **导航 API 分端**:`uni.navigateTo / redirectTo / reLaunch / switchTab`,而不是 `router.push`。
|
||||
- **守卫靠拦截器**:`uni.addInterceptor`,不是路由中间件。
|
||||
- **产物里的路由表**:`window.__uniRoutes`,由 uni-h5 的 `initRouter(app)` 在插件 `install` 阶段拿去 `createRouter`。
|
||||
|
||||
---
|
||||
|
||||
## 3. 改动清单
|
||||
|
||||
### 3.1 短地址改造(第一轮)
|
||||
|
||||
| 文件 | 改动 |
|
||||
| --- | --- |
|
||||
| `manifest.json` | `h5.router.mode = "history"`、`h5.router.base = "/"` |
|
||||
| `pages.json` | `path` 保持带目录(`pages/login/login`);注释指向本文的登记要求 |
|
||||
| `router/routes.ts` | 新增。`PAGE_PATHS` 映射表 + `toRealPath()` / `toCleanPath()` / `pageUrl()` |
|
||||
| `router/h5-clean-url.ts` | 新增。往 `window.__uniRoutes` 追加短地址路由记录 |
|
||||
| `main.ts` | `#ifdef H5` 引入并在模块体求值期调用 `registerCleanUrls()` |
|
||||
| `common/http.interceptor.ts` | `loginPage = toRealPath("login")`;401 跳登录用 `uni.reLaunch({ url: pageUrl("login") })` |
|
||||
|
||||
### 3.2 登录守卫 + 错误页(第二轮,见 §11)
|
||||
|
||||
| 文件 | 改动 |
|
||||
| --- | --- |
|
||||
| `router/routes.ts` | 扩展:错误页登记、`CLEAN_PATH_OVERRIDES`、`AUTH_FREE_PAGES`、`PAGE_PERMISSIONS`、`resolvePageName()` / `errorPageUrl()` / `normalizePath()` |
|
||||
| `router/nav-guard.ts` | 新增。`setupNavGuard()` / `ensureCurrentPageAuthorized()` / `setupUnknownPathFallback()` / `goLoginWithRedirect()` |
|
||||
| `composables/useAuthGuard.ts` | 新增。页面级 `onShow` 复核(补冷启动深链那半边) |
|
||||
| `components/error-panel/error-panel.vue` | 新增。401/403/404 共用外壳(移植旧 `ErrorPanel.vue`) |
|
||||
| `pages/error/page40{1,3,4}.vue` | 新增。三个错误页 |
|
||||
| `pages.json` | 注册 3 个错误页 |
|
||||
| `App.vue` | `onLaunch` 调 `setupNavGuard()` |
|
||||
| `main.ts` | H5 块内 `registerCleanUrls()` 之后调 `setupUnknownPathFallback()` |
|
||||
| `common/http.interceptor.ts` | 非 2xx 分支新增 `statusCode === 403` → 跳 403 页 |
|
||||
| `locale/lang/{zh-CN,en-US}.json` | 新增 `ErrorPage` 组 30 个键 |
|
||||
|
||||
`history` 开关的读取路径:编译层特征开关 `__UNI_FEATURE_ROUTER_MODE__`,取自 `manifest.web || manifest.h5` 的 `router.mode`。
|
||||
|
||||
---
|
||||
|
||||
## 4. 短地址机制:追加克隆记录,而不是挂 alias
|
||||
|
||||
### 4.1 第一版为什么错(实测抓到的真实故障)
|
||||
|
||||
最初实现是给原路由记录挂 `alias: ["/dashboard"]`(数组)。结果症状是**割裂**的:
|
||||
|
||||
- 浏览器地址栏直接输入 `/dashboard` → 能进(vue-router 自己支持数组 alias);
|
||||
- 站内 `uni.reLaunch({ url: "/dashboard" })` → 报 `page \`/dashboard\` is not found`。
|
||||
|
||||
原因在 `node_modules/@dcloudio/uni-h5/dist/uni-h5.es.js` 两条硬约束:
|
||||
|
||||
```text
|
||||
// L1281 getRouteOptions(path, alias = false):uni 的导航层查表
|
||||
return __uniRoutes.find(
|
||||
(route) => route.path === path || route.alias === path // 字符串全等,数组永不命中
|
||||
);
|
||||
|
||||
// L6010 入口页在导航层被强制改写回根路径
|
||||
if (routeOptions.meta.isEntry) {
|
||||
url = url.replace(routeOptions.alias, "/"); // alias 是数组时这行也是错的
|
||||
}
|
||||
```
|
||||
|
||||
结论:**uni 的导航层不读 vue-router 的 alias 能力**,只认 `path` 精确匹配(alias 仅支持字符串全等);且带 `meta.isEntry` 的记录地址永远回落到 `/`,到不了 `/login`。
|
||||
|
||||
### 4.2 现方案
|
||||
|
||||
给每个页面**追加一条克隆路由记录**,`path` 用短地址,让 uni 的查表用 `path` 精确命中。
|
||||
|
||||
| 字段 | 处理 | 原因 |
|
||||
| --- | --- | --- |
|
||||
| `path` | 换成短地址 `/dashboard` | 让 `getRouteOptions` 第一条件命中 |
|
||||
| `alias` | 删除 | 避免与真实路径互相干扰;也避开 `isEntry` 分支里的 `url.replace(alias, "/")` |
|
||||
| `meta.isEntry` | 置 `undefined` | 否则地址栏被强改回 `/` |
|
||||
| `meta.route` | **保持真实路径** | uni 的页面栈、导航栏样式等仍按真实路径查表 |
|
||||
| `isCleanClone` | 标记 `true` | 二次注册时跳过克隆体,防止无限繁殖 |
|
||||
|
||||
### 4.3 时序(为什么能赶上)
|
||||
|
||||
`registerCleanUrls()` 在 `main.ts` 模块体求值期调用,而 `initRouter(app)` 要到插件 `install` 阶段才用**同一个数组引用**去 `createRouter`,所以追加必然被读进去。
|
||||
|
||||
两个分支都要覆盖,因为 pages 虚拟模块与本模块的求值先后不由我们控制:
|
||||
|
||||
- `window.__uniRoutes` 已赋值 → 直接 `applyCleanUrls(数组)`;
|
||||
- 尚未赋值 → `Object.defineProperty` 拦一次 `set`,在赋值瞬间完成注册。
|
||||
|
||||
### 4.4 跨端隔离
|
||||
|
||||
`h5-clean-url.ts` 只被 `#ifdef H5` 引入,实测产物命中数:H5 有、mp-weixin 0、app 0。
|
||||
|
||||
---
|
||||
|
||||
## 5. API 契约(写页面时必须遵守)
|
||||
|
||||
`router/routes.ts` 是页面路径的唯一来源:
|
||||
|
||||
```ts
|
||||
export const PAGE_PATHS = {
|
||||
login: "pages/login/login",
|
||||
dashboard: "pages/dashboard/dashboard",
|
||||
error401: "pages/error/page401",
|
||||
error403: "pages/error/page403",
|
||||
error404: "pages/error/page404",
|
||||
} as const;
|
||||
```
|
||||
|
||||
| 函数 | 用途 | 注意 |
|
||||
| --- | --- | --- |
|
||||
| `pageUrl(name)` | **跳转用的 URL** | H5 返回短地址,其他端返回真实路径(条件编译) |
|
||||
| `toRealPath(name)` | **路径比较**、日志 | 永远带前导斜杠的真实路径 |
|
||||
| `toCleanPath(name)` | 短地址推导 | 优先取 `CLEAN_PATH_OVERRIDES`,否则取路径最后一段 |
|
||||
| `resolvePageName(url)` | 任意 URL → 页面名 | 认不出返回 `null`,守卫据此判 404;`"/"` 视为入口页 |
|
||||
| `errorPageUrl(name, from?)` | 错误页地址 | 内部用 `normalizePath(from)` 清洗后拼进 `?from=`,**不做预编码**(见 §11.4) |
|
||||
| `currentRealPath()` | 当前页真实路径 | 页面栈为空时返回空串 |
|
||||
| `isAuthFree(name)` | 是否免登录页 | 数据源是 `AUTH_FREE_PAGES` |
|
||||
|
||||
硬约束:**`getCurrentPages()` 返回的 `route` 永远是真实路径,不含运行时补的短地址**。所以任何"当前是不是某页"的判断只能用 `toRealPath()`,用 `pageUrl()` 比较在 H5 上会恒不等(`http.interceptor.ts` 的 `goLogin` 就是按这个写的)。
|
||||
|
||||
小程序 / App 端导航只能用真实路径——短地址在那些端根本不存在。
|
||||
|
||||
---
|
||||
|
||||
## 6. 新增页面的完整动作
|
||||
|
||||
```
|
||||
1. src/pages/album/album.vue 文件,目录名必须与文件名同名
|
||||
2. src/pages.json 追加 { "path": "pages/album/album" }
|
||||
3. src/router/routes.ts 的 PAGE_PATHS 追加 album: "pages/album/album"
|
||||
4. src/pages/album/album.vue 受登录保护的页面调一次 useAuthGuard()
|
||||
```
|
||||
|
||||
第 2 步是**绝对必须**:`pages.json` 是编译期唯一注册入口,不写就没有这个页面。
|
||||
第 3 步的必须程度见下一节。
|
||||
第 4 步见 §11:站内跳转由全局拦截器兜住,但冷启动深链不经过任何导航 API,页面自己必须复核。
|
||||
|
||||
命名约定(第 1 步)不是审美问题,是功能要求:短地址由 `toCleanPath()` 取**最后一段**推出来,`pages/album/album.vue` → `/album`;若写成 `pages/album/index.vue`,短地址就成了 `/index`。
|
||||
|
||||
例外:错误页刻意写成 `pages/error/page401.vue`(末段是 `page401`),因为地址要与旧 WebVue 逐字一致的 `/error/page401`,这类情况用 `CLEAN_PATH_OVERRIDES` 指定短地址,而不是把文件改名为 `error/error.vue`。
|
||||
|
||||
---
|
||||
|
||||
## 7. 不登记 PAGE_PATHS 会怎样
|
||||
|
||||
页面**不会坏**,但会失去短地址,并且编译期被拦:
|
||||
|
||||
1. `/pages/album/album` 真实路径照常可访问,深链、刷新、站内跳转都正常;
|
||||
2. 短地址 `/album` 打不进去,控制台输出一条告警(`h5-clean-url.ts`):
|
||||
|
||||
```
|
||||
[h5-clean-url] 页面未登记到 PAGE_PATHS(router/routes.ts),仅真实路径可访问: /pages/album/album
|
||||
```
|
||||
|
||||
3. 只要代码里用 `pageUrl("album")` / `toRealPath("album")`,`PageName = keyof typeof PAGE_PATHS` 会让 TS 在编译期直接报错——这是有意的强制手段,比运行时静默失效应更早暴露。
|
||||
|
||||
另外两种会被跳过的情况(都有独立告警,不会静默):
|
||||
|
||||
- **末段重名冲突**:`pages/a/user.vue` 与 `pages/b/user.vue` 都想占 `/user`,后到者被跳过并提示"短地址冲突,已跳过"。
|
||||
- **tabBar 页面**:明确**不注册短地址**(`h5-clean-url.ts` 的 `isTabBar` 分支)。tabBar 的路径判定依赖 `route.path.slice(1)`,克隆记录会干扰 `switchTab` / `preloadPage`。将来把首页做成 tabBar 时,它只能使用真实路径。
|
||||
|
||||
---
|
||||
|
||||
## 8. 当前没有做的事(已知缺口)
|
||||
|
||||
- **权限守卫只做了一半**:登录守卫与 401/404 已落地(§11),但 `PAGE_PERMISSIONS` 还是空表——旧 WebVue 的 `setupPermissionGuard` 依赖 `PermissionContext.canView`(菜单/功能权限清单),那套体系尚未移植,所以页面级 403 目前只由请求层的 HTTP 403 触发。
|
||||
- **history 模式的服务端兜底**:`/login`、`/dashboard` 这类深链**直接刷新依赖服务端把请求 rewrite 回 `index.html`**。dev server(Vite)已实测返回 SPA 壳,但生产环境(如 `uat.youjiala.com` 的 Nginx)需要加 `try_files $uri $uri/ /index.html;` 一条,否则刷新会 404。这一条尚未在生产环境验证。
|
||||
|
||||
---
|
||||
|
||||
## 9. 验证记录
|
||||
|
||||
改造后跑过的验证,后续再动路由可比照执行:
|
||||
|
||||
- **产物实锤**:`window.__uniRoutes` 含 `{path:"/",alias:"/pages/login/login"}`、`{path:"/pages/dashboard/dashboard"}`,以及追加的 `{path:"/login"}`、`{path:"/dashboard"}` 克隆记录;全程无 `#`。
|
||||
- **浏览器 8/8 用例全绿**:`/`、`/login`、`/dashboard` 深链直达 + 整页刷新;`001/123` 登录后 `reLaunch` 落到 `/dashboard`;上一轮失败的"站内跳 `/dashboard` 报 page is not found"已消失。
|
||||
- **HTTP 层**:`/`、`/login`、`/dashboard`、`/pages/dashboard/dashboard` 均返回含 `<div id="app">` 的 SPA 壳。
|
||||
- **三端构建**:H5 / mp-weixin / app 均 `EXIT=0`;mp-weixin 的 `app.json` 中 `pages = login, dashboard`(根级 path 在小程序端合法)——注意该项属于中间态验证,现行结构为 `pages/xxx/xxx`。
|
||||
- **跨端隔离**:`h5-clean-url` 特征串仅在 H5 产物出现。
|
||||
|
||||
复查命令(PowerShell):
|
||||
|
||||
```powershell
|
||||
# 三端构建
|
||||
.\node_modules\.bin\uni.cmd build *> "$env:TEMP\_b1.log"; "H5_EXIT=$LASTEXITCODE"
|
||||
.\node_modules\.bin\uni.cmd build -p mp-weixin *> "$env:TEMP\_b2.log"; "MP_EXIT=$LASTEXITCODE"
|
||||
.\node_modules\.bin\uni.cmd build -p app *> "$env:TEMP\_b3.log"; "APP_EXIT=$LASTEXITCODE"
|
||||
|
||||
# 产物里的路由表
|
||||
Select-String -Path ".\dist\build\h5\assets\*.js" -Pattern '__uniRoutes=\['
|
||||
```
|
||||
|
||||
浏览器侧的深链 / 地址栏 / 控制台表现无法用构建日志判定,必须真机或自动化实测。
|
||||
|
||||
---
|
||||
|
||||
## 10. 可优化项(未实现,需确认后再动)
|
||||
|
||||
`PAGE_PATHS` 与 `pages.json` 目前是两处手工同步。可改为编译期自动生成,只保留"目录名 = 文件名"一条约定:
|
||||
|
||||
```ts
|
||||
import pagesJson from "@/pages.json"; // uni-app 支持 JSON 导入
|
||||
```
|
||||
|
||||
代价是失去 `PageName` 的字面量类型(跳转目标名不再是编译期校验),且要额外处理 `subPackages`。取舍未定,暂不动。
|
||||
|
||||
---
|
||||
|
||||
## 11. 登录守卫与 401/403/404(移植自旧 WebVue)
|
||||
|
||||
### 11.1 为什么是"两条链路"而不是一条
|
||||
|
||||
旧系统是 vue-router 的 `router.beforeEach`,一个钩子覆盖所有进入方式。uni-app 没有等价物:
|
||||
|
||||
- `uni.addInterceptor(api, { invoke })` 只作用在**调用导航 API 的那一次跳转**上(`navigateTo` / `redirectTo` / `reLaunch` / `switchTab`)。
|
||||
- **冷启动深链不经过任何导航 API**:H5 地址栏直接输入、分享链接打开、小程序"编译进入指定页面",都是 uni 自己解析出页面实例后直接 `onLoad/onShow`,拦截器一次都不会触发。
|
||||
|
||||
所以:`router/nav-guard.ts` 的 `setupNavGuard()` 管站内跳转,`composables/useAuthGuard.ts` 的页面级 `onShow` 复核管冷启动,缺一漏一半。
|
||||
|
||||
取消导航的机制(源码依据,`@dcloudio/uni-h5/dist/uni-h5.es.js`):
|
||||
|
||||
```text
|
||||
invokeApi -> queue(interceptor.invoke, options)
|
||||
queue: res === false 时返回 { then(){}, catch(){} } 这样的空 thenable
|
||||
```
|
||||
|
||||
即 `invoke` 返回 `false` 会让原调用被**静默丢弃**,既不会执行跳转,也不会给调用方留下未捕获的 promise rejection——所以守卫拦下来之后不需要调用方配合处理异常。
|
||||
|
||||
### 11.2 判定顺序与放行表
|
||||
|
||||
`evaluate(url)` 的顺序就是语义:地址不存在(404)→ 未登录(401)→ 无权限(403)。
|
||||
|
||||
- `AUTH_FREE_PAGES` = `login` + 三个错误页。**错误页必须免登录**:401 页就是给"没登录/登录失效"的人看的,拦掉它只会让用户看到一个登录框而不知道刚才为什么被踢。
|
||||
- 登录态只认请求层的 `getJwtInfo()?.AccessToken`(令牌唯一来源,与 `stores/loginProfile` 口径一致)。
|
||||
- 刻意**不**把已登录用户从 `/login` 弹回首页:403 页的"重新登录"按钮需要先能进入登录页。
|
||||
- 跳错误页用 `redirectTo`(保住返回栈,"返回上一页"才有内容),失败再退化成 `reLaunch`。
|
||||
|
||||
### 11.3 错误页与外壳组件
|
||||
|
||||
真实路径 `pages/error/page401` 经 `CLEAN_PATH_OVERRIDES` 得到短地址 `/error/page401`,与旧 WebVue 及 `WebSitePolicy.Web401/403/404` 逐字一致。三个页面共用 `components/error-panel/error-panel.vue`(对应旧 `ErrorPanel.vue`),它只管排版与 `tone` 三色(blue / amber / violet),文案、图标、按钮由各错误页给:
|
||||
|
||||
| 页 | tone | 图标 | 主按钮 | 次按钮 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 401 | blue | `lock-fill` | 去登录(带 redirect) | 返回上一页 |
|
||||
| 403 | amber | `warning-fill` | 重新登录 | 返回首页 |
|
||||
| 404 | violet | `map` | 返回首页 | 返回上一页 |
|
||||
|
||||
旧版用 ant-design 图标和 `<a>`,这里换成 `u-icon` + `u-button`(`u-button` 没有 `icon` 属性,故按钮只有文字),尺寸单位从 px 换成 rpx,头图顶部用 `var(--status-bar-height)` 适配 `navigationStyle: custom`。按钮间距写在容器的 `gap` 上——插槽内容属于各页面的作用域,错误页自己的 scoped 样式选不到它。
|
||||
|
||||
### 11.4 `?from=` / `?redirect=` 不做预编码
|
||||
|
||||
第一版用 `encodeURIComponent` 包了参数值,实测地址栏出现 `?from=%252Fdashboard`——uni 不会先解这一层,而 vue-router 拼 URL 用的是 `encodeURI`(不编码 `/`,但会把 `%` 编成 `%25`),于是被编码两次。现在只做 `normalizePath()` 清洗(剥 query/hash、补前导斜杠),参数值里只剩路径字符,地址栏呈现干净的 `?from=/dashboard`。页面侧仍用 `decodeQueryPath()` 兜底解一次,兼容各端 query 解码时机的差异。
|
||||
|
||||
### 11.5 403 的两个触发点
|
||||
|
||||
- 请求层:`common/http.interceptor.ts` 的非 2xx 分支里 `statusCode === 403` → `goForbidden()` 跳 403 页并带 `?from=`;已经在 403 页就不再跳,避免"错误页自己发的请求又 403"来回刷。
|
||||
- 导航层:等 `PAGE_PERMISSIONS` 与 `PermissionContext` 移植后接在 `evaluate()` 的第三步。
|
||||
|
||||
`goForbidden()` 在请求层自己实现而没有复用 `nav-guard` 的 `jumpToError`:`nav-guard` 依赖请求层读登录态,反向 import 会形成循环依赖。
|
||||
|
||||
### 11.6 词条与验证
|
||||
|
||||
- 文案全部走语言包:`locale/lang/{zh-CN,en-US}.json` 的 `ErrorPage` 组 30 个键;组件内固定的"可能的原因""出错的地址:"也在里面(`reasonsTitle` / `fromLabel`)。
|
||||
- 实测(H5 dev + 自动化浏览器):未登录深链 `/dashboard` → 401 页且 `from` 显示正确;401 的"去登录"→ `/login?redirect=/dashboard`;`001/123` 登录后回跳 `/dashboard`;`/no/such/page` → 404 页;404 的"返回首页"放行;清态后直访 `/error/page403` 不被再拦、无死循环;`window.__uniRoutes` 共 10 条(5 真 + 5 短地址克隆),全程零 console error/warn。
|
||||
- 三端构建 `EXIT=0`;`location.pathname` 仅出现在 H5 产物(`setupUnknownPathFallback` 的 `#ifdef H5` 生效),`page401` 三端产物均有。
|
||||
Reference in new issue
Block a user