feat: 添加 H5 登录调试面板,显示初始 URL 信息并修复布尔值渲染问题
This commit is contained in:
196
docs/login-fix-plan.md
Normal file
196
docs/login-fix-plan.md
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
# H5 登录卡住问题分析与小程序端修复方案
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 一、问题描述
|
||||||
|
|
||||||
|
### 1.1 现象
|
||||||
|
|
||||||
|
从小程序跳转到 H5 页面后,页面一直显示"正在获取登录信息...",无法正常加载首页或引导页。
|
||||||
|
|
||||||
|
### 1.2 调试信息
|
||||||
|
|
||||||
|
H5 调试面板(`?debug=1`)输出:
|
||||||
|
|
||||||
|
```
|
||||||
|
url https://mp.yizhezhe.com/app_03/
|
||||||
|
isWxMp (空 — 实际为 true,因 false 渲染为空)
|
||||||
|
hasTokenParam (空 — URL 中无 token 参数)
|
||||||
|
step wx-postMessage-done (已发送 NEED_TOKEN 请求)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.3 根因分析
|
||||||
|
|
||||||
|
H5 登录流程有三个路径:
|
||||||
|
|
||||||
|
| 分支 | 触发条件 | 预期行为 |
|
||||||
|
|------|---------|---------|
|
||||||
|
| URL 有 token | `?token=***` | 解码 token → 登录 → 跳首页 |
|
||||||
|
| 小程序环境 | 检测到 `MicroMessenger` + `miniprogram` | 发 `postMessage({NEED_TOKEN})` 等小程序响应 |
|
||||||
|
| 浏览器开发 | 其他情况 | 调 `dev-login` 自动登录 |
|
||||||
|
|
||||||
|
当前状态:URL **没有 token**,而是进入了第 2 条路。H5 向小程序发送了 `postMessage({ type: "NEED_TOKEN" })`,但**小程序侧没有监听和处理这条消息**,导致 H5 永远停在"正在获取登录信息..."。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 二、修复方案
|
||||||
|
|
||||||
|
### 方案 A(推荐):打开 web-view 时直接带上 token
|
||||||
|
|
||||||
|
H5 不需要发 `NEED_TOKEN` 消息。每次打开 web-view 时,小程序直接把 token 拼到 URL 里。
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// 小程序侧 — 打开 web-view 的页面中
|
||||||
|
Page({
|
||||||
|
data: {
|
||||||
|
// 小程序自己的 token(通过 wx.login / 微信登录获取)
|
||||||
|
token: 'eyJ0e...Egnk',
|
||||||
|
},
|
||||||
|
|
||||||
|
onLoad() {
|
||||||
|
// 确保 token 已就绪后再打开 web-view
|
||||||
|
this.ensureToken().then(token => {
|
||||||
|
this.setData({ token });
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
async ensureToken() {
|
||||||
|
// 此处用小程序自己的登录逻辑获取 token
|
||||||
|
// 示例:如果已有缓存则用缓存,否则先登录
|
||||||
|
const cached = wx.getStorageSync('auth_token');
|
||||||
|
if (cached) return cached;
|
||||||
|
// 登录逻辑...
|
||||||
|
return token;
|
||||||
|
},
|
||||||
|
|
||||||
|
// WXML: <web-view src="{{webviewUrl}}">
|
||||||
|
get webviewUrl() {
|
||||||
|
const base = 'https://mp.yizhezhe.com/app_03/';
|
||||||
|
const token = this.data.token;
|
||||||
|
// 有 token 就加 ?token=,没有就不加(H5 会 fallback 到 dev-login)
|
||||||
|
return token ? `${base}?token=${encodeURIComponent(token)}` : base;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
**优点:**
|
||||||
|
- 最简单直接,无额外消息通信
|
||||||
|
- H5 拿到 token 后一步到位完成登录
|
||||||
|
- 不需要修改 H5 端代码
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 方案 B:监听 NEED_TOKEN 消息后回传
|
||||||
|
|
||||||
|
如果 web-view 先打开(URL 不带 token),H5 检测到小程序环境后发 `NEED_TOKEN` 消息,小程序接收后把 token 写回 URL。
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// 小程序侧 — web-view 所在的页面
|
||||||
|
Page({
|
||||||
|
onLoad() {
|
||||||
|
// 确保小程序侧准备好接收 postMessage
|
||||||
|
},
|
||||||
|
|
||||||
|
// web-view 的 bindmessage 回调
|
||||||
|
onMessage(e) {
|
||||||
|
const data = e.detail.data || e.detail; // 兼容不同版本
|
||||||
|
console.log('web-view 消息:', data);
|
||||||
|
|
||||||
|
if (data.type === 'NEED_TOKEN') {
|
||||||
|
// 获取 token 并传递回 web-view
|
||||||
|
const token = this.getToken();
|
||||||
|
if (!token) {
|
||||||
|
console.error('token 未就绪,无法响应 NEED_TOKEN');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 方式 1: 重新加载 web-view 的 src(推荐)
|
||||||
|
const base = 'https://mp.yizhezhe.com/app_03/';
|
||||||
|
const webview = this.selectComponent('#webview');
|
||||||
|
// 注意: 小程序 web-view 不支持直接改 src,需要用 navigateTo 重新打开
|
||||||
|
wx.navigateTo({
|
||||||
|
url: `/pages/webview/webview?token=${encodeURIComponent(token)}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 方式 2: 如果 web-view 通过页面参数传递 token(适合小程序自定义导航)
|
||||||
|
// 见方案 A,在打开时直接带上 token
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getToken() {
|
||||||
|
return wx.getStorageSync('auth_token');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
对应 WXML:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<web-view
|
||||||
|
id="webview"
|
||||||
|
src="https://mp.yizhezhe.com/app_03/"
|
||||||
|
bindmessage="onMessage"
|
||||||
|
/>
|
||||||
|
```
|
||||||
|
|
||||||
|
**缺点:**
|
||||||
|
- 需要额外一轮消息通信,H5 会先显示"正在获取登录信息..."再刷新
|
||||||
|
- 小程序 `web-view` 组件没有直接修改 `src` 的 API,只能重新 `navigateTo`
|
||||||
|
- 用户体验不如方案 A 顺滑
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 三、推荐方案对比
|
||||||
|
|
||||||
|
| 维度 | 方案 A(URL 带 token) | 方案 B(消息回调) |
|
||||||
|
|------|-----------------------|------------------|
|
||||||
|
| 实现复杂度 | 低 | 中 |
|
||||||
|
| 用户体验 | 流畅(一步到位) | 有闪烁(页面刷新) |
|
||||||
|
| H5 端改动 | 不需要 | 不需要 |
|
||||||
|
| 小程序端改动 | 加 URL 拼接 | 加 bindmessage 回调 |
|
||||||
|
| 额外通信 | 无 | 一轮 postMessage |
|
||||||
|
| token 过期处理 | 打开时即携带最新 token | 可能携带过期 token 再等 NEED_TOKEN |
|
||||||
|
|
||||||
|
**结论:采用方案 A。**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 四、H5 端问题关联修改
|
||||||
|
|
||||||
|
### 4.1 Debug 面板布尔值修复
|
||||||
|
|
||||||
|
当前 `DebugPanel` 渲染布尔值 `false` 时会因为 `{v || "(空)"}` 显示为空。需要改:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
// 当前(有 bug):
|
||||||
|
<td>{v || <span className="text-gray-500">(空)</span>}</td>
|
||||||
|
|
||||||
|
// 修复后:
|
||||||
|
<td>{v === false ? 'false' : v === true ? 'true' : v || <span className="text-gray-500">(空)</span>}</td>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.2 `isWechatMiniProgram()` 废弃
|
||||||
|
|
||||||
|
如果小程序采用方案 A(打开时直接带 token),H5 不再需要 `isWechatMiniProgram` 检测和 `NEED_TOKEN` 消息发送逻辑。上线后可清理:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// LoginPage.tsx — 删除以下代码段
|
||||||
|
if (isWechatMiniProgram()) {
|
||||||
|
try {
|
||||||
|
(window as any).wx?.miniProgram?.postMessage({ type: "NEED_TOKEN" });
|
||||||
|
} catch {}
|
||||||
|
setError("正在获取登录信息...");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 五、验证步骤
|
||||||
|
|
||||||
|
1. 小程序侧按方案 A 修改,确保打开 web-view 时 URL 带 `?token=***`
|
||||||
|
2. 访问 `https://mp.yizhezhe.com/app_03/?token=***&debug=1`
|
||||||
|
3. 检查调试面板:`hasTokenParam=true`、`step=decode-ok` 或 `step=fetch-ok`
|
||||||
|
4. 确认跳转到 `/home` 或 `/onboarding`
|
||||||
|
5. 关闭 `?debug=1` 正常使用
|
||||||
|
6. (可选)清理 DebugPanel 和 `isWechatMiniProgram` 相关代码
|
||||||
@@ -9,6 +9,7 @@
|
|||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
<script>window.__INITIAL_URL__ = document.URL;</script>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
<script type="module" src="/src/main.tsx"></script>
|
<script type="module" src="/src/main.tsx"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ const INIT_DEBUG: DebugInfo = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function DebugPanel({ info }: { info: DebugInfo }) {
|
function DebugPanel({ info }: { info: DebugInfo }) {
|
||||||
|
const [initialUrl] = useState(() => (window as any).__INITIAL_URL__ || '(N/A)');
|
||||||
return (
|
return (
|
||||||
<div className="absolute top-0 left-0 right-0 z-50 bg-black/80 text-white text-xs font-mono p-3 text-left max-h-[60vh] overflow-auto">
|
<div className="absolute top-0 left-0 right-0 z-50 bg-black/80 text-white text-xs font-mono p-3 text-left max-h-[60vh] overflow-auto">
|
||||||
<div className="font-bold text-yellow-400 mb-1">🐞 调试信息 (关闭需去掉 ?debug=1)</div>
|
<div className="font-bold text-yellow-400 mb-1">🐞 调试信息 (关闭需去掉 ?debug=1)</div>
|
||||||
@@ -41,9 +42,13 @@ function DebugPanel({ info }: { info: DebugInfo }) {
|
|||||||
{Object.entries(info).map(([k, v]) => (
|
{Object.entries(info).map(([k, v]) => (
|
||||||
<tr key={k} className="border-b border-white/10">
|
<tr key={k} className="border-b border-white/10">
|
||||||
<td className="pr-2 text-gray-400 w-28 align-top whitespace-nowrap">{k}</td>
|
<td className="pr-2 text-gray-400 w-28 align-top whitespace-nowrap">{k}</td>
|
||||||
<td className="break-all text-green-300">{v || <span className="text-gray-500">(空)</span>}</td>
|
<td className="break-all text-green-300">{v === false ? 'false' : v === true ? 'true' : v || <span className="text-gray-500">(空)</span>}</td>
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
|
<tr className="border-b border-yellow-400/50">
|
||||||
|
<td className="pr-2 text-yellow-400 w-28 align-top whitespace-nowrap font-bold">initialUrl</td>
|
||||||
|
<td className="break-all text-yellow-300">{initialUrl}</td>
|
||||||
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user