Zhouruizhe: 修复了首页空白 以及定位漂移到大西洋的bug #14

Merged
zhouruizhe merged 5 commits from zhouruizhe into main 2026-08-03 11:25:02 +08:00
Member
No description provided.
zhouruizhe added 5 commits 2026-08-03 11:19:39 +08:00
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
微信主包上限 2 MB,30 张 800×600 照片单独就 2.6 MB,
整包 3.2 MB,上传被拒(错误码 80051)。

按使用场景把照片拆成两层:
- 主包缩略图 400×300 q75(678 KB),供地图、行程、打卡卡片使用,
  这些场景最大只显示 192rpx(≈288 px @DPR3),800×600 是过剩
- 分包大图 800×600 q66(1832 KB),供详情页 hero 使用,
  随 pages-poi 分包按需下载

两层都保持 4:3,PoiImage 的 aspectFill 行为不变,视觉表现与之前一致。

详情页从 pages/poi/detail 移到 pages-poi/detail:主包页面无法引用
分包资源,hero 要用大图,页面就必须在分包内。新增
coverPhotoFullUrlFor() 供分包内页面取大图,占位图仍指向主包。

verify-weixin-output.mjs 增加防回归检查:分包注册、两层照片数量
一致、主包与各分包实际体积(留 64 KB 余量)。包体再超限会在
verify 阶段失败,不必等上传才发现。

process-poi-photos.mjs 重写为双层产出,幂等,末尾报告包体占用并
在超限时退出非零。原图改从 src/photo-intake/ 读取(不提交仓库)。

改造后:主包 1522 KB、分包 pages-poi 1847 KB,均在 2048 KB 内。
mp-weixin 与 H5 均编译通过,97 个测试通过,vue-tsc 与 ESLint 无错。
三个问题,前两个各有独立根因。

1) 首次打开定位到几内亚湾、底图变成 HERE

   regionchange 处理里读的是 `event.detail.longitude`,但微信的 regionchange
   载荷没有这个字段,`Number(undefined)` 恒为 NaN,于是每次都落到异步的
   getCenterLocation。冷启动时地图 SDK 还没拿到有效中心点,它会回传 (0, 0),
   而两个 store 的坐标校验只查 Number.isFinite,(0, 0) 直接通过。

   (0, 0) 落在几内亚湾(Null Island),微信 <map> 一到境外坐标就切 HERE 底图。
   全程不需要用户点过定位按钮。

   数据集校验器本来就在拒绝零坐标,只是运行时 store 没套用同一条规则。现把它
   提成共享守卫 isTrustworthyCoordinate,validateCoordinates 改为复用,并铺到
   map store、location store 和打卡定位服务。regionchange 一侧改为优先读微信
   真正给的 detail.centerLocation,回落路径校验后才写入,且 mapReady 为 false
   时不接受任何回写。

2) 冷启动白屏、左上角只剩回首页的小房子

   f8ad5e1 把详情页从 pages/poi/detail 移进 pages-poi 分包,源码调用点都改了,
   但旧路由本身从 app.json 消失。任何还攥着旧路径的入口 —— 开发者工具模拟器上次
   停留的路由、预览启动页、已发出的分享卡片或二维码 —— 冷启动会落到微信解析不出
   的路由,渲染成白屏;因为不是 tabBar 页且栈深为 1,左上角只剩小房子。

   旧路径补一个只做重定向的跳板页,带 poiId 就 redirectTo 新路径,不带就 reLaunch
   回地图。用 redirectTo 是为了让跳板不留在页面栈里。主包 +1 KB。等所有入口都刷新
   过可以删掉。

3) 跳转详情页时地图仍可拖动几毫秒

   <map> 是渲染在 webview 之上的原生组件,销毁是异步的,页面转场开始了它还在。
   navigating 之前只用于防重复点击,没接到地图上。现在接上 enable-scroll /
   enable-zoom,并给 selectMarker、openCheckInRecords 补同一个守卫(后者原先完全
   没有防护),regionchange 在跳转期间也不再回写 viewport。onShow 里直接复位
   navigating,返回时立刻恢复交互,不等 500ms 兜底定时器。

   原生组件的异步销毁消不掉,但那几毫秒里地图不再响应拖动和缩放。

验证:测试 100 passed(map store 新增零坐标、越界、resetViewport 兜底用例),
type-check 干净。build 主包 1187 KB / 分包 1841 KB,dev 1527 KB / 1847 KB,
均过 verify:mp-weixin。
fix(map): 收敛 regionchange 回写回路,修复选中标点后地图抽动
AI Code Review / review (pull_request) Successful in 2m11s
7ef4094788
选中标点后地图在左右/上下反复抽动,根因是视口回写形成了自激回路:
map 的 longitude/latitude/scale 绑定到 store,而 regionchange 又无条件把
地图当前中心点写回 store —— 写 store 触发地图移动,移动结束回传一个
「差一点点」的中心点,再写回,再移动。上一个提交把中心点改成从
detail.centerLocation 同步读取后,回路里原本靠 getCenterLocation 异步跳变
掩盖住的这一点噪声就直接闭合了。

两道闸:
- 只接受手势造成的变化。causedBy 为 update(我们自己改绑定值或调
  includePoints 触发)时 store 已是权威值,不回写。老基础库拿不到
  causedBy 时退化到下一道闸。
- 所有回写走 commitViewport,经 isSignificantViewportChange 过滤掉小于
  1e-4 度 / 0.01 级的变化。远小于任何一次真实拖动,足以吸收量化噪声。

顺带修一个 H5 构建回归(1bcdd40 引入):uni 的 H5 路由生成器按路径推导
组件标识,pages/poi/detail 与 pages-poi/detail 都归一成 PagesPoiDetail,
重复声明导致 build:h5 失败。兼容跳板必须留在旧路径上,所以改名分包路由
pages-poi/detail -> pages-poi/poi-detail。
chore: 删除 files/ 下的三份方案文档
AI Code Review / review (pull_request) Successful in 4m45s
57ba418630
本机没装 git-lfs,仓库里留下的是 130 字节的 LFS 指针而不是文档本体,
对代码和构建都没有作用。按需要直接从工作树移除。

.gitattributes 里的 LFS 规则保留,它约束的是以后新增的二进制文件。
Owner

我已经阅读了完整的 diff、实际的源码文件以及项目的约定。这是审查结论。

🔍 审查结论

本次 PR 主体是三件事:① POI 详情页移入 pages-poi 分包 + 照片分两层(主包缩略图 / 分包大图)以躲开微信 2 MB 主包上限;② 收敛地图 regionchange 回写回路,修复选中标点后地图抽动;③ 统一 (0,0) Null-Island 坐标的拒绝逻辑。代码注释详尽、思路清晰,并配套了 verify-weixin-output.mjs 产物校验与两个 vitest 用例。其余 files/ 下 58 个删除是归档/打卡旧代码清理,属无害改动。

严重问题(必须修复)

  • 未发现必须修复的阻断性缺陷。地图回写自激、坐标降级、分包路由这三处的核心逻辑经逐文件核对是自洽的:commitViewportisSignificantViewportChange 的阈值门 + causedBy 过滤(src/pages/map/index.vue:341)确实能切断「写 store → 地图动 → regionchange → 写 store」的回路;isTrustworthyCoordinatesrc/domain/poi/validation.ts)在 validation / location store / location service / map 页四处复用且行为一致;旧路径 pages/poi/detail 降级为 redirectTo 跳板,且所有调用方(map/assistant/itinerary/check-in)均已改指 pages-poi/poi-detail,无遗漏。

建议改进

  • scripts/process-poi-photos.mjs:1import { existsSync } from 'node:fs' 写在了文件顶部文档注释之前(注释从第 2 行才开始)。属纯风格问题,建议把这条 import 挪到注释块之后、与其余 import 合并,符合本文件其它 import 的排布习惯。
  • scripts/process-poi-photos.mjsMAIN_PACKAGE_LIMIT_KB = 2048 对「主包缩略图」做超限判断(thumb.kb >= MAIN_PACKAGE_LIMIT_KB)语义偏松:缩略图与 JS/CSS/其它静态资源共用主包 2 MB 预算,单看缩略图没超 2 MB 并不代表整体安全。真正的兜底是 verify-weixin-output.mjs 里对整包 mainPackageKb > 1984 的检查,这里不会出 bug,但建议把脚本里的提示文案 / 阈值与「整包」口径对齐,避免误导后人以为缩略图自身可达 2 MB。
  • src/pages-poi/poi-detail.vue:24heroImages computed)只把 coverImageId 对应那张图替换成分包大图,其余图片仍用主包 400×300 缩略图。若某 POI 的 images 有多张,hero 画廊里非封面帧会被放大到 750×422rpx 出现明显糊感/质量跳变。当前 POI 数据若普遍只有一张封面图则无影响;建议确认数据形态,多图时要么全部走大图、要么只展示封面单图。
  • src/pages.json 末尾无换行(diff 末行 \ No newline at end of file)。与其它 JSON 配置风格不一致,建议补一个尾换行。
  • src/pages/poi/detail.vue:29 跳板 redirectTo 时对 rawPoiId 做了 encodeURIComponent,而新详情页 onLoad 又对 query 取值做了 decodeURIComponent。由于现有 poiId 均为 ASCII(poi_*),编解码是恒等无副作用;但这是「编码 → 解码 → 再编码 → 再解码」的两段链路,若日后 poiId 出现 % / 中文等字符会被双重解码出错。非当前阻塞,记录为潜在脆弱点。

测试与验证建议

  • 跑测试:新增 test/map-viewport.test.tstest/map-store.test.ts 三个用例(Null-Island 拒绝、reset 兜底、阈值门)覆盖到位。本机当前无 node_modules,CI 里需确认 vitest 能跑通。
  • 分包大图运行时路径coverPhotoFullUrlFor 返回 /pages-poi/static/photos/<id>.jpgsrc/data/poi/photos.ts:73),需在微信开发者工具 + 真机确认分包页面能正确按需加载该路径(分包未下载时不应被主包页面引用)。verify-weixin-output.mjs 只校验了文件存在与两层张数一致,校验不了运行时 src 解析。
  • 包体上传:用「微信开发者工具 → 预览/上传」实测主包与 pages-poi 分包均未触顶(脚本设了 1984 KB 安全线,但真机上传才是最终判据)。
  • 地图抽动手测:选中标点 → 等 regionchange end 回写 → 观察地图是否仍反复微动;并验证拖动后返回页面能恢复到用户拖到的位置(确认 commitViewport 没有把真实拖动误判为噪声丢弃)。
  • 跳转期间锁交互navigating 期间 enable-scroll/enable-zoom=falseselectMarker 提前返回(src/pages/map/index.vue:246:662 附近),手测点击标点跳详情、再快速点打卡记录入口的连点场景,确认无重复入栈/白屏。
  • 旧路径跳板:构造一个指向 pages/poi/detail?poiId=xxx 的入口(模拟已发出的分享卡 / 模拟器上次停留路由)冷启动,确认经 redirectTo 落到新详情页而非白屏。
我已经阅读了完整的 diff、实际的源码文件以及项目的约定。这是审查结论。 ## 🔍 审查结论 本次 PR 主体是三件事:① POI 详情页移入 `pages-poi` 分包 + 照片分两层(主包缩略图 / 分包大图)以躲开微信 2 MB 主包上限;② 收敛地图 `regionchange` 回写回路,修复选中标点后地图抽动;③ 统一 `(0,0)` Null-Island 坐标的拒绝逻辑。代码注释详尽、思路清晰,并配套了 `verify-weixin-output.mjs` 产物校验与两个 vitest 用例。其余 `files/` 下 58 个删除是归档/打卡旧代码清理,属无害改动。 ### 严重问题(必须修复) - 未发现必须修复的阻断性缺陷。地图回写自激、坐标降级、分包路由这三处的核心逻辑经逐文件核对是自洽的:`commitViewport`→`isSignificantViewportChange` 的阈值门 + `causedBy` 过滤(`src/pages/map/index.vue:341`)确实能切断「写 store → 地图动 → regionchange → 写 store」的回路;`isTrustworthyCoordinate`(`src/domain/poi/validation.ts`)在 validation / location store / location service / map 页四处复用且行为一致;旧路径 `pages/poi/detail` 降级为 redirectTo 跳板,且所有调用方(map/assistant/itinerary/check-in)均已改指 `pages-poi/poi-detail`,无遗漏。 ### 建议改进 - `scripts/process-poi-photos.mjs:1` 的 `import { existsSync } from 'node:fs'` 写在了文件顶部文档注释**之前**(注释从第 2 行才开始)。属纯风格问题,建议把这条 import 挪到注释块之后、与其余 `import` 合并,符合本文件其它 import 的排布习惯。 - `scripts/process-poi-photos.mjs` 里 `MAIN_PACKAGE_LIMIT_KB = 2048` 对「主包缩略图」做超限判断(`thumb.kb >= MAIN_PACKAGE_LIMIT_KB`)语义偏松:缩略图与 JS/CSS/其它静态资源**共用**主包 2 MB 预算,单看缩略图没超 2 MB 并不代表整体安全。真正的兜底是 `verify-weixin-output.mjs` 里对**整包** `mainPackageKb > 1984` 的检查,这里不会出 bug,但建议把脚本里的提示文案 / 阈值与「整包」口径对齐,避免误导后人以为缩略图自身可达 2 MB。 - `src/pages-poi/poi-detail.vue:24`(`heroImages` computed)只把 `coverImageId` 对应那张图替换成分包大图,其余图片仍用主包 400×300 缩略图。若某 POI 的 `images` 有多张,hero 画廊里非封面帧会被放大到 750×422rpx 出现明显糊感/质量跳变。当前 POI 数据若普遍只有一张封面图则无影响;建议确认数据形态,多图时要么全部走大图、要么只展示封面单图。 - `src/pages.json` 末尾无换行(diff 末行 `\ No newline at end of file`)。与其它 JSON 配置风格不一致,建议补一个尾换行。 - `src/pages/poi/detail.vue:29` 跳板 `redirectTo` 时对 `rawPoiId` 做了 `encodeURIComponent`,而新详情页 `onLoad` 又对 query 取值做了 `decodeURIComponent`。由于现有 poiId 均为 ASCII(`poi_*`),编解码是恒等无副作用;但这是「编码 → 解码 → 再编码 → 再解码」的两段链路,若日后 poiId 出现 `%` / 中文等字符会被双重解码出错。非当前阻塞,记录为潜在脆弱点。 ### 测试与验证建议 - **跑测试**:新增 `test/map-viewport.test.ts` 与 `test/map-store.test.ts` 三个用例(Null-Island 拒绝、reset 兜底、阈值门)覆盖到位。本机当前无 `node_modules`,CI 里需确认 `vitest` 能跑通。 - **分包大图运行时路径**:`coverPhotoFullUrlFor` 返回 `/pages-poi/static/photos/<id>.jpg`(`src/data/poi/photos.ts:73`),需在微信开发者工具 + 真机确认分包页面能正确按需加载该路径(分包未下载时不应被主包页面引用)。`verify-weixin-output.mjs` 只校验了文件存在与两层张数一致,校验不了运行时 src 解析。 - **包体上传**:用「微信开发者工具 → 预览/上传」实测主包与 `pages-poi` 分包均未触顶(脚本设了 1984 KB 安全线,但真机上传才是最终判据)。 - **地图抽动手测**:选中标点 → 等 `regionchange end` 回写 → 观察地图是否仍反复微动;并验证拖动后返回页面能恢复到用户拖到的位置(确认 `commitViewport` 没有把真实拖动误判为噪声丢弃)。 - **跳转期间锁交互**:`navigating` 期间 `enable-scroll/enable-zoom=false`、`selectMarker` 提前返回(`src/pages/map/index.vue:246`、`:662` 附近),手测点击标点跳详情、再快速点打卡记录入口的连点场景,确认无重复入栈/白屏。 - **旧路径跳板**:构造一个指向 `pages/poi/detail?poiId=xxx` 的入口(模拟已发出的分享卡 / 模拟器上次停留路由)冷启动,确认经 `redirectTo` 落到新详情页而非白屏。
zhouruizhe merged commit 0a2af5ef3d into main 2026-08-03 11:25:02 +08:00
Sign in to join this conversation.
No Reviewers
No labels
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: team/gmTouringMiniApp#14