fix(frontend): scroll-view 卸载后仍写 scrollTop——给 uni-h5 补空判(dev-board#349) - #690
Merged
Merged
Conversation
卡上猜是 ChatInterface.vue 的 `scrollTop.value += 10000` 与 FileStagingArea.vue。 读了框架源码 + 抓了真实堆栈,两个都不是: - 崩的那行是 `uni-h5.es.js:14370` 的 `main.value.scrollTop = val`,属于 `_scrollTopChanged` 里**不带动画**的那条分支。ChatInterface 的 scroll-view 显式设了 `:scroll-with-animation="true"`,走的是另一条 `scrollTo` 分支,压根到不了这一行。 - FileStagingArea 的 scrollTop 从初始化后再没被写过。 - 调用方是 `14483`:`onMounted(() => nextTick(() => _scrollTopChanged(scrollTopNumber.value)))` ——**挂载时**框架自己排的那一次补写,与我们代码里写不写 scrollTop 无关。 也就是说:任何 `<scroll-view scroll-y>` 只要在同一批 flush 里挂载又卸载(v-if 分支翻转、 路由离开),这个 nextTick 回调跑到时 `main.value` 已经是 null,就崩。复现路径上是 统一设置页 AdminPane 里 PersonalWorkLogPanel 的 `<scroll-view v-else scroll-y>`(loading / 空 / 有数据三分支)。两条错误同一个根因: Vue 打出 TypeError,nextTick 的 promise 链再把它变成一条未处理 rejection, 被 main.js 的全局兜底打成第二条。 框架层没有空判,而我们**无法取消**它已经排好的 nextTick,只能补这道守卫。语义上也正是 对的:元素都没了,本来就没有什么可滚的。照 desktop/scripts/patch-dmg-builder.js 的既有 套路,加 frontend/scripts/patch-uni-h5-scrollview.mjs + frontend package.json 的 postinstall,对 uni-h5.es.js / uni-h5.cjs.js 各加三处真值判断:两条 `_scroll*Changed` 的直写,外加动画分支 `scrollTo` 的入口(聊天区自动滚动就在那条路上,同一个卸载竞态会崩在 container.scrollWidth,一并守住)。锚点在文件内必须唯一,对不上就 exit 1 让安装失败—— 升级 uni-h5 后报「结构已变」时先看新版是否已自带空判。 **没有动 ChatInterface.vue 一个字**:卡上担心的「改坏聊天区自动滚动」这条风险不存在。 回归护栏加在 tests/app-e2e/run.mjs 结尾:控制台异常信号整体不判死(历史噪音多,一刀切 会天天红),但 `setting 'scrollTop'` 这一条单拎出来判死——不然补丁掉了也只是报告里多两行 没人看。 冷启动隔离环境(后端 9797 + dev server 5175,配方见 eng-infra): - 定点探针走「项目列表页 →(真实鼠标点击)个人中心」:**改前 2 条信号,改后 0 条**, 加载项目列表页本身 0 条(信号确实来自这次跳转)。 - 打补丁后全量 `npm run test:app-e2e`:**131 步通过 / 0 失败**,scrollTop 信号 0 条。 剩下 4 条异常信号是 1 条 info + 2 条环境 skip + 1 条 J1 解锁门上的既有 console 错误 (出现在跳设置页之前,与本改动无关)。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
zeweihan
force-pushed
the
fix/uni-h5-scrollview-null-guard-349
branch
from
September 1, 2026 04:14
b35f1b5 to
5a93f0a
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
修 dev-board#349。
根因与卡上的猜测不一样
卡上猜触发点是
ChatInterface.vue的scrollTop.value += 10000(约 1214 行)与FileStagingArea.vue。读了框架源码并核对堆栈行号后,两个都不是:uni-h5.es.js:14370的main.value.scrollTop = val,位于_scrollTopChanged里不带动画的那条分支。ChatInterface的 scroll-view 显式设了:scroll-with-animation="true"(ChatInterface.vue:248),走的是另一条scrollTo分支,压根到不了这一行。FileStagingArea.vue的scrollTop从data()初始化成 0 之后,全文件再没有任何一处写它。14483:也就是说:任何
<scroll-view scroll-y>只要在同一批 flush 里挂载又卸载(v-if分支翻转、路由离开),这个nextTick回调跑到时main.value已经是null,就崩。复现路径上的那个是统一设置页AdminPane.vue:1124渲染的PersonalWorkLogPanel.vue:36——<scroll-view v-else scroll-y>,挂在 loading / 空 / 有数据三分支上。两条错误是同一个根因:Vue 打出 TypeError,
nextTick的 promise 链再把它变成一条未处理 rejection,被main.js:31的全局兜底打成第二条。修法
框架层没有空判,而我们无法取消它已经排好的
nextTick,只能补这道守卫;语义上也正是对的——元素都没了,本来就没有什么可滚的。照desktop/scripts/patch-dmg-builder.js的既有套路加一个postinstall补丁(frontend/scripts/patch-uni-h5-scrollview.mjs),对uni-h5.es.js/uni-h5.cjs.js各加三处真值判断:_scrollTopChanged的直写;_scrollLeftChanged的直写;scrollTo的入口 —— 聊天区自动滚动就在这条路上,同一个卸载竞态会崩在container.scrollWidth,一并守住。锚点在目标文件里必须唯一,对不上就
exit 1让安装失败(升级 uni-h5 后报「结构已变」时先看新版是否已自带空判,是就删掉补丁与钩子)。产物侧靠npm ci触发 postinstall ——ci.yml的 frontend job 与desktop-build.yml的 Build frontend 步骤都会跑到。没有动
ChatInterface.vue一个字:卡上担心的「改坏聊天区自动滚动这条高频 UI 路径」这条风险不存在。回归护栏
加在
tests/app-e2e/run.mjs结尾。控制台异常信号整体不判死(历史噪音多,一刀切会天天红),但setting 'scrollTop'这一条单拎出来判死 —— 不然补丁哪天掉了也只是报告里多两行没人看。验证(冷启动隔离环境:后端 9797 + dev server 5175,配方见 eng-infra)
定点探针,真实鼠标点击走「项目列表页 → 个人中心」:
未处理的 Promise rejection+TypeError: Cannot set properties of null (setting 'scrollTop'))全量
npm run test:app-e2e,两轮对照:摘补丁时校验过三个锚点各自唯一、改后形态存在。两轮的 131/0 一致,说明滚动行为没坏——差异只在这两条信号上。
(打补丁那轮多出的
[console] JSHandle@error出现在 J1 首启解锁门,在跳设置页之前;还原病灶那轮它没出现,是条间歇性的既有信号,与本改动无关。)复测提示
打开开发者工具跳一趟「项目列表页 → 设置」,控制台不应再出现
Cannot set properties of null (setting 'scrollTop')与随之而来的未处理 rejection;聊天区发消息后仍应平滑滚到底。🤖 Generated with Claude Code