核心判断是DOMContentLoaded不能成为首屏可用依赖;弱网时仅保留LCP元素最小DOM,非首屏内容用占位符延迟挂载,深度嵌套容器扁平化,fetch配AbortController与1500ms超时,关键CSS内联且≤14KB,剥离动画及媒体查询,禁用@import。

低速网络下,HTML 页面不能靠“等资源慢慢来”,必须按执行单元分级裁剪和加载——核心判断只有一条:DOMContentLoaded 不能成为首屏可用的依赖节点。
怎么结构化裁剪 DOM 以适配弱网
弱网时 HTML 解析快,但 JS 执行和资源加载严重滞后;若初始 HTML 包含大量非首屏节点(如 <footer>、<aside>、深度嵌套的 <div>),会加剧 layout thrashing,甚至阻塞主线程。
-
<main>内只保留 LCP 元素所需最小 DOM:标题、主图(带loading="eager")、核心操作按钮 - 所有首屏外区域统一用占位符,例如
<div data-lazy="comments"></div>,不挂载真实内容 - 禁用任何
data-autoplay、data-init类自动触发逻辑;改由显式状态控制,仅当networkStatus === 'good'时调用initComments() - 对深度 >3 的嵌套容器(如
div > div > div > article)用display: contents扁平化,避免无意义 wrapper 触发额外 layout
fetch 必须带 AbortController 和响应时间感知
onload/onerror 在弱网下完全不可靠:请求可能卡在 pending 状态不回调,或响应头已返回但 body 拖延数秒才到,导致降级严重滞后。
- 所有资源加载必须封装为
fetch()+AbortController,超时设为1500ms,主动中断而非等待自然超时 - 不只看 HTTP 状态码;连续两次
response.time > 800ms即标记为弱网,触发简化模式(如跳过推荐模块、关闭动画) - 离线包加载失败后,回退前先检查
navigator.onLine;若为false,直接启用内联 fallback 数据(如预置 JSON 或空数组) - 避免在
DOMContentLoaded阶段集中发起多个fetch;用节流控制(如每 30 秒最多一次探测)防并发压垮连接
关键 CSS 内联 ≠ 整个 style.css 塞进去
把整个 style.css 内联进 <head> 是典型误区——弱网下大体积 CSS 仍阻塞渲染,且无法卸载;真正的“关键 CSS”仅指 LCP 元素所需的 layout、color、font-size 等基础样式。
立即学习“前端免费学习笔记(深入)”;
- 用
critters或类似工具提取 LCP 元素所需 CSS,内联进<style></style>;原始体积建议 ≤14KB(gzip 后约 4–8KB) - 其余 CSS 用
<link rel="preload" as="style">预加载,加载完成后用sheet.replaceSync()替换 - 所有动画关键帧(
@keyframes)和复杂媒体查询(如@media (prefers-reduced-motion))必须从关键 CSS 中剥离,延迟加载或条件注入 - 禁止在
<style>中使用@import——它会触发额外 HTTP 请求并阻塞解析
为什么 loading="lazy" 不是万能开关
原生懒加载只对 <img> 和 <iframe> 生效,且默认触发距离视口约 1250px,不是“所有图片都该加”。
- 首屏 Hero 图、轮播图第一张、关键按钮图标等,必须禁用
loading="lazy",否则可能被当成非关键资源延迟加载 - 所有加
loading="lazy"的<img>必须显式设置width和height属性,否则滚动时加载会引发布局偏移(CLS) - Safari 15.4+ 才支持
loading="lazy"对<iframe>的支持;旧版安卓 WebView 可能完全忽略,需降级为data-src+IntersectionObserver - 不要同时写
src和loading="lazy"再额外用 JS 监听——浏览器可能先加载src,造成重复请求
真正难的不是加几个属性,而是把每个资源加载行为都变成可中断、可降级、可感知的执行单元。比如一个 fetch 超时后,你得立刻知道该展示什么 fallback、是否要重试、要不要降级 UI 交互方式——这些决策点,往往藏在 DOM 结构和资源加载策略的交界处,最容易被忽略。



















