抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

App 端首次切换底部 Tab 时,整页闪一下。问题前后试错三轮,最后确认根因是页面首次创建期间的空白帧,用预热方案绕过。本文记录现象确证、排除过程、最终修复与验证。

一、现象:先确证,再动手

排查任何闪烁类问题的第一步是把现象钉死,而不是直接改代码。通过真机观察确证三点:

  1. 只在每个 Tab 页面首次被创建时闪,之后切回同一 Tab 不再闪——与”切换”动作无关,与页面首次创建绑定;
  2. 冷启动进首页不闪。首页那次页面创建被启动图(splash)盖住了——这条是关键线索,说明框架创建页面期间确实会露出一个空白窗口;
  3. 闪光约 1 帧,快于眨眼,肉眼无法分辨帧内内容。

现象本身已经指向结论:闪的不是某个页面的渲染内容,而是”页面还没渲染出来”的那一帧。

二、排除:四条直觉方向全部走死

以下四条是此类问题的常见成因,每条都做过真机验证,全部排除:

方向 验证方式 结果
字体 font-display: swap 的 FOUT 确认使用 MiSans 之前问题已存在 排除
backdrop-filter 首帧合成 全局类(.liquid-glass、页头、TabBar 等)强制 none 后复测 闪依旧
图片解码 日历/明细首帧不渲染任何栅格图,分类 PNG 是接口返回后才绑定 排除
窗口背景色 逐页 app-plus.background 设为与页面底色复合后同色的 #f0f3f7 闪依旧

另有两个排查中的坑值得记录:

  • 闪光太短,改颜色定位法(把底色改成纯红看闪是否变红)无效,只能靠”闪还在不在”的二选一判断;
  • 页面底色 $bg-page 是 rgba(240, 243, 247, 0.85),压在窗口背景 #f0f3f7 上复合后正好还是 #f0f3f7——肉眼看到的”同色”掩盖了层级关系。

三轮试错的共同教训:没有先确认那一帧显示什么就直接改代码,每次都白跑一轮。第一节的确证就是对这个错误的纠正。

三、定位:两处真实的首帧空窗

排除干扰项后,定位到两个实际产生空窗的位置:

1. TabBar 切换时机制造中间帧

原实现点击 Tab 后先让胶囊动画到目标位置、改写 activeTab,再延迟 400ms 执行 switchTab。这 400ms 里画面是”旧页面内容 + 新 Tab 高亮”,路由一切换旧内容消失、目标页首帧未出,空窗叠加放大。

2. 目标页首帧没有可渲染内容

  • H5 端 Tab 页首次点击才下载 JS/CSS chunk(日历页约 314KB,大头是 lunar-javascript),下载期间页面无可渲染内容;
  • 明细页仅以 summary 接口返回决定是否退出骨架,其它并行接口未返回时出现真实卡片、空卡位分阶段出现的画面;
  • 日历首访同步执行当月全部日期的农历换算,首帧可能只提交选中日期背景;
  • App 端页面创建期间露出 WebView 背景,页面 CSS 生效前是默认白底。

四、修复:预热为主,配合时机与兜底

核心思路:把每个 Tab 的”首次创建”提前到用户点击之前。

4.1 预热(utils/preload-pages.js)

  • H5:首页 onReady 后用 uni.preloadPage 串行预热日历、明细、我的三个 chunk。首页数据请求先发 600ms,之后每页间隔 200ms,避免预热大文件抢首屏带宽;
  • App-vue:没有 preloadPage 能力,改为 splash 覆盖下真实 switchTab 逐个创建三个 Tab 再回首页——每页等待 onReady(单页超时 1.5s 即继续),整体 8 秒兜底关闭 splash,任何一页异常都不阻塞启动。通过 alwaysShowBeforeRender:false + autoclose:false 保持 splash 显示;
  • 首次登录:登录页已关闭 splash,登录成功后执行 warmupTabPagesAfterLogin(),由各 Tab 共用的全屏准备遮罩覆盖预热切换过程,完成后回首页撤销遮罩;
  • 小程序端不支持 preloadPage,直接跳过。

4.2 切换时机

TabBar 点击后立即执行 switchTab,不再提前改写 activeTab;选中态由目标页 onShow 的 sync() 回填实际路由,消除”旧内容 + 新高亮”中间帧。

4.3 首帧收敛与背景兜底

  • 统计数据增加首次完整加载状态与请求去重,骨架保持到全部并行请求收敛后整体替换为真实卡片;图表卡片 onReady 后再挂载,同尺寸骨架占位;
  • 日历先绘制完整月历结构,就绪后再补农历与节日文字,降低首帧计算负担;
  • .page-wrapper 使用不透明页面底色,pages.json 为 App 原生父窗体设置同色背景——即使页面创建期间也不露 WebView 白底。

五、验证与总结

H5 与 App 双端真机验证:已有 token 冷启动由 splash 覆盖预热、清除登录态后由准备遮罩覆盖预热,两种情况下首次点击三个 Tab 均不再闪屏。

三条可复用的结论:

  1. 闪烁类问题先确证现象——“首次创建才闪”和”冷启动首页不闪”两条现象直接决定了排查方向,比任何代码分析都快;
  2. 已排除的方向要留档,三次试错都耗在同一批无效方向上,正是因为没有记录;
  3. 页面首次创建的空白帧是框架层行为,难以根治,但”在用户看到之前创建完毕”(预热 + splash/遮罩覆盖)是通用的绕过手段——H5 用 preloadPage,App 用真实 switchTab,本质相同。

灌水


灌水