网页视觉优化:7个被架构师忽略的关键细节
|
跟前端吵了一架。上周三下午,产品把PRD甩过来,说“视觉首屏要秒开”,前端老张截图发我——白屏三秒,LCP 4.7 秒,抖得像信号不良的车载电台。我盯着那个水印一样的加载骨架图看了两秒,顺手点了刷新。第五回了。咖啡凉了,杯底一圈浅褐色印子,像谁用铅笔画了个圈。
文章配图,仅供参考 我原以为是 SSR 渲染链路断了,结果扒了一圈,后端压根没动 HTML 模板,就扔了个空壳子过去,连 preload 都没加,全靠前端在 componentDidMount 里 fetch,fetch 之前先等 Vue 初始化,初始化前还得等 webpack runtime 加载……你猜 runtime 文件叫啥?叫 app.b64239a5.js。这命名谁起的?问运维小李,他敲着键盘说:“哦,是去年八月份上线时前端自己配的,说哈希能防缓存。”我说:“哈希是防缓存,但你让浏览器第一次请求得先下载 800K 的 runtime,再解包,再执行,再拉数据,再 render,这不叫防缓存,这叫防用户体验。”他抬头看了我一眼,没接话,键盘声突然响得像在敲钢板。第二个细节:字体加载。咱们所有项目 CSS 里都写了 @font-face,src: url(/fonts/zh-regular.woff2) format('woff2'); 但没写 font-display: swap。去年八月份上线的那个营销页,上线三天,IOS 用户投诉“字是歪的”,不是渲染错位,是文字根本不出,只留方框。排查了两天,最后发现 Safari 对 woff2 的 fallback 行为特别绝——它不等字体加载完,也不显示后备字体,就卡着空白,直到超时或手动刷新。我查 MDN 翻到一半手机亮了一下,是 HR 推来的 JD,标题写着“急招懂字体性能的前端架构师”。我关了页面,没点开。这种事不写进 SLA,不走工单系统,可每天都有人默默把网页关掉。 第三个:图片的 decoding 属性。你见过 工单系统又弹了条消息:“【P1】首页 LCP 波动大(iOS 17.5)”,我划掉,没点开。反正点开也是看指标曲线,看不出哪个 div 在假装自己是按钮。 第四个,也最荒诞:CSS 中的 will-change。我们所有「悬停动效」组件,不管动不动,全写了 will-change: transform。产品经理提过一次,“为什么鼠标移到菜单上,页面会闪一下?”前端老张回得很快:“是 will-change 触发了图层提升,没问题。”可问题不在“有没有问题”,在你把整个 header 都 promote 成独立图层后,Android WebView 内存回收直接失灵。去年八月份那个裂口,就是某次热更新后,header 下多挂了两个动态插槽,图层叠了七层,WebView GC 看见就想重启。运维重启了三回,日志里全是 “Out of memory: Kill process 12731 (WebCore)”——不是我们写的 Java 进程,是浏览器自己干的。我翻了翻监控,那段没埋点,连告警都没设。 窗外天黑了。楼下一排灯亮起来,光晕糊成一片。其实我到现在也没搞懂:为什么 CSS will-change 声明在 iOS Safari 里会抑制合成器合并?文档写得含糊,WebKit bug tracker 里翻出二十个同名 issue,有人标 “fixed”,有人标 “wontfix”,最后一个回复是 2020 年 11 月,说 “behavior is intentional per spec draft”。spec draft 是哪版?没写。 第五个,纯属习惯性漏掉:prefetch 关键资源时,忘了带 as=“script” 或 as=“style”。我们有个核心脚本叫 main-vendor.chunk.js,CDN 缓存命中率挺高,可 prefetch 它的时候写的是 ,没写 as,浏览器就当普通 document 处理,进不了脚本预加载队列。结果首屏 JS 执行比 DOMContentLoaded 晚了三百多毫秒——不多,但够让一个 loading spinner 多转半圈。这个延迟,在测试环境根本测不出来,因为本地磁盘读太快。真机测?前端说:“QA 用的是 iPhone 12,我试过 iPhone 13,应该差不多。”我说:“iPhone 12 和 13 的 I/O 调度器不是一个 kernel 版本。”他说:“啊?真不一样?” 第六个,我其实不确定它是不是关键,但每次看到都心虚:CSS 里写了 { box-sizing: border-box },可全局 reset 之前,有个第三方 UI 库的 style.css 先加载了。它里面写了 button { box-sizing: content-box }。你猜最终按钮宽高算谁的?我们所有表单页,按钮右边总偏移 2px。修过三次,每次 PR 都被合进不同分支,最后同时存在三个修复 patch,互相覆盖。上次发布后,产品截图问我:“提交按钮怎么比输入框矮一点?”我截图发给老张,他回:“嗯……可能盒子模型冲突,我晚上看看。”然后我就再没见过他提这个 MR。 第七个,我不太敢说,因为可能被当成甩锅:FID 测出来很高,归因到 “main thread blocking”,但主线程上跑的大部分代码,其实是监控 SDK 插入的。某个埋点库在 DOMContentLoaded 后自动绑定 click 事件,用的是 document.addEventListener('click', handler, true),capture 阶段抢跑了。它不耗时,但它阻塞了后续所有 capture 阶段的事件处理,包括 React 的事件代理。我们测过删掉这个 SDK,FID 降了三分之一。现在呢?产品说不能关,因为转化漏斗要统计“曝光即点击”。我说:“那让它别用 capture。”他们回复:“SDK 不开源,升级要等商务排期。”排期?上个版本合同签的是 2022 年。去年八月份我们想换,法务拦住了,说“license 条款变更需重新过合规”。我听了没说话,只把茶杯挪远了点——杯底那圈印子已经干透了。 说白了,优胜劣汰这事,不是系统挑人,是用户用手指投票。刷一下没反应,就划走;抖一下卡两帧,就关掉;等三秒还是白屏,下次干脆不输网址。咱们写的 Java 微服务扛得住十万 QPS,可浏览器只给你一次机会——它不会 retry,不会熔断,不会重试三次再 fallback,它就静静关掉标签页,连错误码都不报。我打算明天找个真机,装个 Network Link Conditioner,把 4G 网络模拟成 3G+ 高丢包,然后打开首页录个 performance timeline。要是还能复现那几个抖动帧……我就去找前端,不聊方案,就坐他旁边,看他敲键盘,等他自己看出哪一行 style 变更触发了 layout thrashing。 (编辑:站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |


吗?没人加。我们所有 CMS 生成的 img 标签,默认 decoding="sync",浏览器必须同步解码,堵死主线程。上个月运维半夜打电话问我:“为啥凌晨两点内存尖刺?堆栈全卡在 libjpeg 上?”我没敢说是我们的头图组件在首页轮播里用了 6 张未压缩 WebP,还开着 lazyload——lazyload 是懒了,但 onl oad 回调一触发,浏览器啪一下全塞进解码队列。后来我搜了下公司内部基建库,image-utils.js 里有一行注释:// decoding 支持 IE 不全,先注释掉。注释时间:2021 年 3 月。这代码还在跑。