返回博客

平台敏感 Bug:复制可工作的兄弟实现,比理论推断快 10 倍

有一类 bug,你把所有 Web 知识都用上了,还是修不好。滚动穿透、sticky 失效、iOS 弹窗抖动、WXML 事件不触发……它们有一个共同属性:平台敏感。修它们的正确姿势,和你平时 debug 的直觉完全相反。

现象

某 C 端小程序里,一个帮助中心弹窗打开后,背景页面依然可以滚动——经典的滚动穿透问题。

第一轮修复(凭"事件冒泡"理论):给遮罩层加 catch:touchmove 拦截,弹窗内改用 <scroll-view> 让内容可滚。真机测试:背景仍可滚 ❌。

第二轮修复(在错误模型上叠 patch):猜测是 page 本身的滚动没锁,加 <page-meta page-style="overflow:hidden">。真机测试:还是能滚 ❌。

第三轮(换思路):有人提示——"另一个页面的帮助弹窗早就没这个问题,参考它"。grep 出来、完整读下来、1:1 复刻。真机测试:一次通过 ✅。

两轮失败加一轮命中,代价如下:

| 路径 | 试错轮次 | 真机验收 | 引入新 bug 风险 | |------|:---:|:---:|:---:| | 凭理论推断 | 2 | 2 | 高(第 1 轮反向加重了穿透)| | 抄已工作版本 | 1 | 1 | 0 |

根因

为什么理论推断在这里失败?

微信小程序的滚动穿透不是单一维度的问题,它是多维耦合<scroll-view> 是 native 组件,touch 事件不向上冒泡;<page-meta> 锁的是 page-scroll,管不到 native scroll chaining 释放的 delta;catch:touchmove 只拦截了 WebView 层的事件……每一维单独看都"应该有效",但多维耦合之后,任何一维遗漏都导致穿透。

通用 Web 知识无法穷举这些维度的相互作用。而同项目里已经工作的兄弟实现,穿越过了所有维度——它是活体文档,在真机上被验证过的组合。

坑:对平台敏感 bug 凭理论单点修补

在平台积累知识(微信小程序 / iOS Safari / 老 Vue 2 / WXML)面前,通用 Web 理论的命中率经验值低于 30%。每轮"有理论依据的修复"都有可能在修 A 维度的同时破坏 B 维度,越修越乱。连续 2 轮失败后继续凭理论叠 patch,是在复利放大误差。

正解

修平台敏感 bug 的标准流程:

第 1 步:先 grep,不先想。

按 bug 类型在同项目 grep 相关关键字:

# 弹窗滚动穿透
grep -r "catch:touchmove" miniprogram/pages/
grep -r "page-meta" miniprogram/pages/
grep -r "overflow: hidden" miniprogram/pages/

第 2 步:找到功能相似的兄弟页面,完整 Read 它的所有相关文件(.wxml + .wxss + .js),不要只读片段——多文件协同的细节往往就是关键。

第 3 步:列差异表。

| 维度 | 兄弟(工作)| 当前 bug 页面 | |------|------|------| | 容器结构 | view + view + view | view + scroll-view + view | | 滚动方式 | CSS overflow-y | native scroll-view | | 锁滚机制 | page-meta + catchtouchmove | 仅 catchtouchmove |

差异表本身就是修复 checklist。

第 4 步:1:1 复刻,不省略任何差异项。

"这个差异感觉不重要"——平台坑就是因为某一项你"觉得不重要"才会暴露。全部对齐,修完后再考虑抽公共组件。

正解:把已工作的实现当第一参考源

修平台敏感 bug 的第一步不是"分析根因",是"grep 同项目功能相似的已工作实现"。找到后完整读下来,列差异表,逐项对齐。复制粘贴不是"不优雅"——它的本质是用已被真机验证过的代码,而不是用推断出来的代码。

一句话外卖

平台敏感 bug 面前,经验复刻胜过理性推断——同项目里已经工作的兄弟实现,是穿越过所有平台暗坑的活体文档,直接抄。