5.3 HTML 性能优化


5.3 HTML 性能优化

本节摘要:页面性能的主战场在加载链路——浏览器拿到 HTML 后按序拉取资源,谁的顺序错了谁就阻塞渲染。本节讲三大方向:加载链路(渲染阻塞与资源提示)、图片策略(懒加载、尺寸声明、现代格式)、脚本调度(defer 与 async 的区别),外加指标常识与"先测量后优化"的方法论。

学习目标

阅读完本节,你应当能够:

  1. 描述浏览器从拿 HTML 到首屏呈现的资源加载顺序;
  2. 说出样式表为何阻塞渲染、脚本为何阻塞解析,以及各自的标准解法;
  3. 用原生懒加载与宽高声明优化图片;
  4. 区分 defer 与 async 的执行时机并正确选择;
  5. 用"先测量后优化"的流程避免乱优化。

加载链路:谁在挡路

浏览器渲染一个页面的简化流程:下载 HTML → 解析构建 DOM → 遇到外部资源(样式、脚本、图片)按各自规则处理 → DOM 与 CSSOM 就绪 → 渲染呈现。性能问题的根源是这条链路上的两种阻塞:

样式表阻塞渲染。浏览器不知道样式就敢画页面,用户会看到"裸页闪一下再穿衣服"(第 3 章讲过),所以样式表在就绪前渲染被挂起。这就是样式表必须放 head 且尽早加载的原因——它阻塞 unavoidable,只能让它短。

脚本阻塞解析。脚本可能修改 DOM(第 5.2 节的全部操作),浏览器在脚本下载与执行期间暂停 HTML 解析。同步脚本放在 head 里,等于让整页解析等一个脚本下载——这是性能的头号经典错误。解法见本节"脚本调度"。

<head> <meta charset="UTF-8"> <title>加载顺序示范</title> <link rel="stylesheet" href="site.css"> <!-- 阻塞渲染,放前面让它尽早开始 --> <script src="app.js" defer></script> <!-- defer:下载并行、执行延后 --> </head>

资源提示是精细调度的工具:preload 提前下载当前页必定要用的关键资源(字体、首图);preconnect 提前建立到第三方域的连接(握手耗时省下来);fetchpriority 给关键图片标注高优先级。这些提示不改行为,只调顺序——锦上添花,先保证基础顺序正确再上提示。

图片:体量大头的三板斧

图片通常占页面体积的大头,优化收益也最大。三板斧按投入产出比排序:

懒加载——首屏外的图片先不加载。原生支持一行属性:

<img src="photo.jpg" alt="产品图" loading="lazy" width="800" height="533">

loading 属性设为 lazy,浏览器在图片接近视口时才发起下载。首屏主视觉图不要懒(它就是首屏,懒了反而延迟),长列表长文章里的图全部懒。

宽高声明——width 与 height 写进标记。浏览器据此在图片下载前就预留正确空间,避免"文字先渲染、图片到达后把内容挤下去"的布局跳动。这个一行属性的成本换的是可感知体验的大项。

格式与响应式——现代压缩格式(WebP、AVIF)同画质体积小一到七成,配合 picture 的多源供给(第 2 章 source 的图片版)按浏览器能力下发;srcset 与 sizes 按屏幕尺寸给不同分辨率,手机不下桌面图。

<picture> <source srcset="hero.avif" type="image/avif"> <source srcset="hero.webp" type="image/webp"> <img src="hero.jpg" alt="首屏主视觉" width="1600" height="900"> </picture>

picture 内的多个 source 按序探测:认识 AVIF 用 AVIF、否则 WebP、都不到退到 img 的 JPG。注意格式切换时 type 值要逐个核对——image/webp 拼错会导致整条源失效,这类笔误只在真实浏览器测试时暴露。

装饰性图片还有第四板斧:能进 CSS 背景的别用 img。图标、花纹、渐变这类无语义的视觉元素由样式层承载,标记保持干净(第 2 章 alt 分级的延伸)。

脚本调度:defer 与 async

脚本加载的三种模式,区别只在"下载是否并行、执行何时发生":

写法 下载 执行 适用
无属性(同步) 阻塞解析 下载完立即,继续前 几乎不用
defer 并行下载 DOM 解析完成后、按序 应用主脚本
async 并行下载 下载完立即,可能打断解析 独立统计类脚本

defer 是应用脚本的默认答案:下载不挡路、执行时 DOM 已就绪(不需要套"等文档就绪"的监听)、多个 defer 按书写顺序执行(依赖关系有保障)。async 适合完全独立、不碰 DOM、顺序无所谓的脚本——统计埋点是典型。内联小程序脚本(无外部文件)不受这两个属性影响,就近执行。

<script src="vendor.js" defer></script> <!-- 先声明,先执行 --> <script src="app.js" defer></script> <!-- 依赖 vendor,顺序有保障 --> <script src="analytics.js" async></script><!-- 独立统计,尽快执行 -->

指标与测量:先看数据再动手

性能是可量化工程,三个核心指标构成"用户体感"的画像:最大内容绘制(首屏最大元素何时出现,体感"多快看到")、首次输入延迟(页面何时能响应交互,体感"多快能用")、累积布局偏移(页面内容跳不跳,体感"稳不稳")。浏览器工具与开源检测服务都能给出这三项,优化前先测,定位短板再对症。

"先测量后优化"不是流程洁癖,是因为性能优化的直觉经常错位:肉眼觉得慢的轮播图可能根本不在关键路径,真正拖慢首屏的是 head 里一个同步脚本或一张未压缩首图。测量工具的水瀑布图把每个资源的下载顺序、阻塞关系摊开——第 5.2 节"知道哪里有成本再优化"的原则,在页面级就是看瀑布图。

指标与测量:先看数据再动手

HTML 自身的瘦身

三件小事常被忽略。其一,空白与注释——生产环境压缩去掉缩进与注释,体积减两到三成,交给构建工具自动做。其二,文档别挂多余框架——一个静态介绍页引整个组件库的脚本,是最常见的"杀鸡用牛舰队"。其三,关键内容前置——首屏内容在 HTML 里靠前,流式解析下用户更早看到内容(浏览器不等全部下载完就开始渲染前半部分),把首屏标记写在前面本身就是优化。

动手实验:亲眼看一次阻塞与解放

<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>阻塞实验</title> <style>body { font-family: sans-serif; }</style> <script> // 模拟一个耗时脚本:同步执行会卡住后面所有内容的解析 // const start = Date.now(); while (Date.now() - start < 2000); </script> </head> <body> <h1>阻塞实验</h1> <p>把上面那行注释解开,刷新页面:这行文字会延迟约两秒才出现。</p> <p>然后把整个脚本块换成外部文件加 defer 属性(内容同上),</p> <p>文字立即出现,脚本在解析完成后才执行。</p> </body> </html>

按注释指引做两个对照:解开死循环注释刷新——页面卡住两秒;把它改成 defer 的外部脚本——文字即时出现。两秒钟的差距就是"同步脚本挡路"的体感版。再用开发者工具的瀑布图分别看一次,资源的阻塞关系在图上一目了然。

⚠️ 常见坑:给所有脚本无脑加 async。多脚本有依赖时(组件库依赖、主脚本依赖库),async 的乱序执行会随机报"找不到定义"——偶现、难复现、只在慢网络暴露。有依赖就 defer,独立才 async,不确定就 defer。

首屏关键的进阶手段

三板斧之上的进阶手段,两个最常用。关键样式内嵌:把首屏必需的一小段样式直接写进 head 的 style 块,其余样式异步加载——首屏渲染不再等外部样式表往返。代价是首屏样式要人工维护一份内嵌副本,适合首页这类流量大、首屏要求苛刻的页面。资源提示的组合:预连接提前与第三方域握手(省下建立连接的两三个往返)、预加载把关键字体或首图提前排进队列。提示类的共同特点是零风险——浏览器只是获得更优的调度信息,用错了顶多没有收益。

移动端还有一条标记层的一行收益:viewport 元信息里的渲染提示(如避免未适配页面的临时缩放抖动),第 2 章媒体查询一节已经配过,这里从性能角度再确认一遍它的存在——适配与性能在这一行属性上会师。

感知性能:快不快与觉不觉得快

同一件事的另一面:用户对"快"的判断很大程度是感知问题。骨架屏(内容到达前先画出页面轮廓)让等待有了形状,观感等待时间显著缩短;进度指示、按钮点击的即时反馈,都在降低"卡住了吗"的焦虑。这些手段不减少任何毫秒,却直接改善体验评分。感知优化的原则与布局防跳动同源:不确定性比慢更伤体验——布局偏移指标之所以与加载时长并列为三大核心指标,正是因为"内容跳来跳去"的页面即使加载很快,体感也很糟。图片宽高声明、骨架屏、字体加载策略,全是这条原则的落地。

⚠️ 常见坑:为消失了几百毫秒的白屏做骨架屏,为本来就三行的列表做虚拟滚动。感知优化也要先测量体感瓶颈在哪——指标里布局偏移为零的页面,骨架屏的收益约等于零。优化前问一句:指标短板是哪一项,比看竞品有什么就抄什么有效。

高频疑问两则

问:第三方脚本(统计、客服、广告)拖慢页面怎么办?
三步:能 defer 或 async 全加上;能延迟加载的(客服组件)改成用户交互或滚动后再插入;第三方域名加预连接。彻底手段是自托管关键统计,但维护成本要权衡。

问:单页应用首屏慢,是 HTML 的问题吗?
通常是脚本体积与执行时间的问题,但解法回到 HTML 层:服务端渲染让首屏内容直接在 HTML 里(第 4.3 节 SEO 的同一条答案)、代码分割减少首屏脚本、关键样式内嵌。这也再次说明分层知识是通的——性能问题常在别的层暴露,解法落回 HTML。

问:优化应该做到什么程度为止?
给两条线:指标线(三大核心指标全绿)是工程合格线;投入产出线是决策线——每再做一项优化前,问它能把哪个指标提升多少,答不出量级的优化让位给别的需求。性能优化有边际递减,头部两项优化通常吃掉八成收益,尾部的精雕细琢收益微小却引入复杂度。"够快"是工程结论,不是极限运动的成绩。

本节要点回顾

(优化的最大敌人是没有测量数据的焦虑式调参,指标在手,每一步优化都有回声。)

  • 两种阻塞要分清:样式表阻塞渲染(前置解决),同步脚本阻塞解析(defer 解决)。
  • 图片三板斧:loading 懒加载、宽高声明防跳动、现代格式加响应式;装饰图进 CSS。
  • defer 是应用脚本默认答案:并行下载、DOM 就绪后按序执行;async 只给独立脚本。
  • 三大体感指标:多快看到、多快能用、稳不稳——先测量定位短板再动手。
  • 感知优化同样重要:骨架屏与即时反馈降低焦虑感,不确定性比慢更伤体验。
  • HTML 自身瘦身:压缩、别挂多余框架、首屏内容前置。

优化的优先级排序也再确认一遍:先加载链路(脚本 defer 与样式前置,零成本收益最大)、再图片三板斧(属性级改动)、最后才是资源提示与感知优化(需要设计与打磨)。从上往下做,每一层做完都重新测一次指标,确认收益落袋再进下一层。

下一节是第二条约束:安全——注入攻击与防线。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U