关键渲染路径怎么优化?阻塞渲染的CSS和JS拖慢首屏的机制与实战

关键渲染路径怎么优化?阻塞渲染的CSS和JS拖慢首屏的机制与实战
张文保 15 分钟阅读 2,953 阅读
本文目录
  1. 关键渲染路径到底是什么?浏览器从HTML到首屏经历了几步?
  2. 为什么CSS默认会阻塞渲染?它凭什么让整页白屏?
  3. 我那个外贸客户的首页,为什么开着开着白屏两秒?
  4. 关键CSS到底指什么?怎么把它挑出来内联?
  5. 剩下那些非关键CSS,怎么异步加载不阻塞?
  6. JavaScript又是怎么拖慢首屏的?async和defer该怎么选?
  7. 怎么快速判断自己的关键渲染路径堵在哪?
  8. 关键渲染路径优化和LCP、TTFB是什么关系?别搞混了
  9. 外贸客户那个首页,最后压到了多少?
  10. 内联关键CSS有没有副作用?什么时候不该用?
  11. 常见问题解答
  12. 关键CSS一定要手工提取吗?
  13. 内联关键CSS会不会让HTML文件变得很大?
  14. async和defer到底怎么选?
  15. 关键渲染路径优化对SEO到底有没有帮助?
  16. 用了HTTP/2,还需要合并CSS、优化关键路径吗?
  17. 单页应用(SPA)也适用这套优化吗?
  18. 权威参考资料

摘要:关键渲染路径,说的是浏览器从拿到HTML到在屏幕上画出第一屏像素,中间必须走完的那几步:解析HTML建DOM、解析CSS建CSSOM、合成渲染树、布局、绘制。这条路上有两个最爱拖后腿的家伙——默认阻塞渲染的CSS,和会卡住解析的同步JavaScript。把首屏真正用得上的那一小撮CSS拎出来内联进head,其余的异步加载;再给脚本安排好defer或async,首屏就能快得多。这篇把这条路径拆开,讲清楚每一步卡在哪、怎么疏通。

我是保哥,做SEO二十多年,帮不少海外独立站治过“打开慢”的毛病。很多人一说页面慢,第一反应就是换个快点的服务器、上个CDN。可我见过太多案例,服务器响应飞快、首字节秒到,页面却还是白屏好几秒。问题不在服务器,在浏览器拿到HTML之后那段路——关键渲染路径。

这段路是每个页面都绕不开的必经之路,偏偏又最容易被塞满障碍。今天我就把它一步步拆开,告诉你浏览器到底在忙什么、哪几步最容易卡壳,以及怎么让首屏早点亮起来。

关键渲染路径到底是什么?浏览器从HTML到首屏经历了几步?

你在地址栏敲下回车,服务器把HTML吐回来,故事才刚开始。浏览器要把这坨文本变成你眼睛看到的画面,中间得走一条固定的流水线,这条线就叫关键渲染路径。

MDN对关键渲染路径的拆解,它大致分五步:先解析HTML构建DOM树,再解析CSS构建CSSOM树,然后把这两棵树合成渲染树(只留下要显示的节点),接着做布局算出每个元素的位置和大小,最后绘制成屏幕上的像素。

关键在于,这五步里有先后依赖:没有CSSOM,浏览器不敢开始渲染,因为它不知道每个元素长什么样。所以只要CSS没加载解析完,整条流水线就卡在那儿等着,屏幕上什么都不显示。这也是为什么CSS常被叫做阻塞渲染的资源——它不快点就位,用户就得对着白屏干瞪眼。想更深入地看浏览器抓取和构建DOM的全过程,我在 DOM抓取渲染索引三阶段里画过更细的流程图。

为什么CSS默认会阻塞渲染?它凭什么让整页白屏?

有人会问,浏览器为什么不先把内容显示出来、CSS到了再套样式?其实早期浏览器真这么干过,结果就是臭名昭著的无样式内容闪烁——先蹦出一堆黑乎乎的默认样式文字,CSS一到又整个重排一遍,画面跟抽风似的。用户体验极差。

为了避免这种尴尬,现代浏览器选择了保守策略:先等CSS就位、把样式算准了再渲染。宁可让你多等一小会儿白屏,也不让你看那一下难看的闪烁。这个取舍本身没错,问题出在很多站的CSS又大又慢——一个几十上百KB的全站样式表,塞满了首屏根本用不到的规则,却硬是拦在渲染前面。

所以优化关键渲染路径,核心就一句话:别让浏览器为了它暂时用不上的东西干等。首屏要用的样式,赶紧给它、越快越好;首屏用不上的,往后稍稍,别挡道。Chrome官方对阻塞渲染资源的审计说明里,把这条路上该消除的障碍列得清清楚楚,值得对照着自查。

我那个外贸客户的首页,为什么开着开着白屏两秒?

说个真事。前两年一个做工业配件的外贸客户找我,说他们的英文站在海外打开特别肉,尤其首页,用户点进来要盯着白屏两秒多才见内容。他们自己查过,服务器在欧洲有节点,首字节返回很快,实在想不通慢在哪。

我用Lighthouse一跑就看明白了:罪魁是一个82KB的全站CSS文件,同步加载、阻塞渲染。这个文件把整站所有页面的样式——产品页的、表单页的、博客的——全打包在一起,首页首屏其实只用得上其中很小一块,可浏览器必须把这82KB整个下载、解析完,才敢画第一个像素。海外用户网速一波动,这一下就是两秒起步。

更糟的是,head里还挂着几个同步的第三方脚本,一个统计、一个客服插件,个个都卡在HTML解析中间。浏览器解析到那儿就得停下来等脚本下载执行,等于在本就拥堵的路上又设了几道卡。首屏想快都快不起来。

客户当时还反问我一句:那我不是上了CDN吗,图片、脚本都走CDN了,怎么还慢?我给他打了个比方:CDN好比把货仓搬到了离你家更近的地方,取货是快了,可你家门口那条必经的小路要是堵着一辆抛锚的大卡车,货再近也进不来。那辆抛锚的卡车,就是阻塞渲染的CSS和同步脚本。不把它拖走,光把货仓搬近没用。这话他一下就听懂了。

关键CSS到底指什么?怎么把它挑出来内联?

治这种病的第一招,叫关键CSS。所谓关键CSS,就是渲染首屏(用户一进来不滚动就能看到的那部分)真正需要的最小样式子集。它通常只有全站CSS的一小块,几KB到十几KB而已。

做法是把这一小撮关键CSS直接内联写进HTML的head里,用style标签包起来。这样浏览器拿到HTML的同时就拿到了首屏样式,不用再为它发一次网络请求、多跑一个来回,CSSOM瞬间就能建好,首屏立刻能渲染。web.dev对提取关键CSS的实践指南里介绍了critical、penthouse这类工具,能自动帮你把首屏用到的规则抽出来,不用手工一条条挑。

手工挑当然也行,但站一大就不现实了。更靠谱的是把这类工具接进构建流程,每次发布自动重新提取,免得样式改了、关键CSS却还是旧的,那就白优化了。CSS-Tricks对首屏关键CSS编写的讨论里也提醒过,关键CSS不是一劳永逸的,它得跟着页面结构一起维护。

剩下那些非关键CSS,怎么异步加载不阻塞?

首屏关键CSS内联了,剩下那一大坨非关键样式怎么办?总不能不加载,页面往下滚、交互起来还得用它们。答案是异步加载——让它们在后台悄悄下载,不挡首屏渲染的道。

经典手法是用link标签配合preload:把CSS用rel=preload、as=style的方式预获取,加载完了再通过onload事件把它切换成正式的样式表应用上去。这样这份CSS的下载就不阻塞渲染了。preload这套资源提示的用法,我在 五种资源提示技术怎么给关键资源提速里专门拆过,用对了能让浏览器提前、并行地把该拿的资源拿到手。

还有个更取巧的老办法,是拿media属性做文章:给非关键CSS的link标签写上一个不匹配当前设备的media值,浏览器就会把它当成低优先级、不阻塞渲染地加载,等加载完再改回正常的media。手法虽然有点野,但兼容性好、可靠,很多性能工具至今还在推荐。

这里得提醒一个常见的翻车点:异步加载非关键CSS,可能会让页面在关键CSS和完整CSS之间有一小段样式不全的过渡。如果你的关键CSS提取得准,首屏该有的样式都在里面,用户是察觉不到的;可一旦漏提了某个首屏元素的样式,就会看到它先以半成品的样子出现、CSS一到才补全,反而制造了新的视觉跳动。所以关键CSS宁可稍微多提一点,也别漏了首屏可见的东西——这跟治布局偏移的思路其实是相通的,首屏的东西必须一次到位。

JavaScript又是怎么拖慢首屏的?async和defer该怎么选?

CSS之外,另一个拖后腿的大户是JavaScript。默认情况下,浏览器解析HTML时一碰到script标签,就得停下来把脚本下载、执行完,才继续往下解析。这叫解析阻塞,脚本挂在head里又大又慢时,首屏内容根本轮不到出场。

解药是async和defer两个属性。async是脚本下载不阻塞解析,但下载完立刻执行(会打断解析),适合那种互相独立、跟页面结构没关系的脚本,比如统计。defer是下载不阻塞解析、且要等HTML解析完了才按顺序执行,适合依赖DOM、又讲究先后顺序的脚本。

一个简单的判断法:跟首屏渲染没关系的脚本,能defer就defer,能扔到body底部就扔底部,别让它堵在head里。我那个外贸客户的统计和客服脚本,就是全部改成异步、挪出关键路径,光这一下首屏就松快了不少。

怎么快速判断自己的关键渲染路径堵在哪?

光讲原理不落地没用,说说怎么自查。最省事的入口还是Lighthouse——跑一遍性能报告,重点看“消除阻塞渲染的资源”这一项,它会把卡在关键路径上的CSS和JS一个个列出来,还标了各自大概拖慢了多少毫秒。这份清单就是你的施工图,从拖得最狠的那个开始拆。

再深一层,可以打开Chrome DevTools的网络面板,把“瀑布图”调出来看。首屏渲染之前,浏览器到底串行等了哪几个请求,一目了然。你会经常发现一些意想不到的堵点:一个挂在head里的第三方字体CSS、一段同步加载的A/B测试脚本,它们个头不大,却死死卡在关键路径的咽喉上。Smashing Magazine对消除阻塞渲染CSS、优化体积与性能的梳理里,把这些容易被忽略的隐形堵点归了类,照着排查能省不少事。

还有个我常给客户的土办法:拿真机、开着弱网模拟,亲眼盯着页面加载一遍。数据是冷冰冰的毫秒,但用户的憋屈是真真切切的。你亲眼看着首屏白那么两秒,比看一百个报告都更有动力去把它治好。别老在你那台千兆光纤的开发机上测,那不是你用户的真实处境。

关键渲染路径优化和LCP、TTFB是什么关系?别搞混了

这几个概念老被混为一谈,得理清楚。TTFB管的是服务器那一段——从点击到浏览器收到第一个字节有多快,属于HTML还没到手之前的事,这条线我在 TTFB与多层缓存怎么一起治里讲过。关键渲染路径管的是HTML到手之后、浏览器把它变成画面这一段。两段首尾相接,共同决定了用户多久能看到内容。

而LCP是个结果指标,它衡量的是最大那块内容多久显示出来。TTFB慢会拖累LCP,关键渲染路径堵了也会拖累LCP——它俩都是LCP的上游。所以你要压LCP,得顺着这条链子往上查:先看服务器段的TTFB,再看浏览器段的关键渲染路径,别只盯着最大那张图使劲。具体怎么系统地压LCP,我在 六层架构把LCP从四秒压到一秒半里从头到尾走了一遍。

外贸客户那个首页,最后压到了多少?

回到那个白屏两秒的首页。我们的组合拳是这样打的:先用工具把首页首屏的关键CSS提取出来,大约11KB,内联进head;剩下那70多KB全站CSS改成preload异步加载;head里的第三方脚本全部换成defer或async,挪出关键路径。

改完再跑一遍,首屏内容出现的时间(FCP)从2.1秒掉到了0.9秒,LCP也跟着改善了一大截。海外用户那边最直观的反馈是,页面不再白屏干等,点进来内容几乎立刻就有了。跳出率跟着降了一小截,这对一个靠首页承接广告流量的外贸站来说,是实打实的钱。

这事给我的体会是:性能优化别一上来就砸钱换硬件。很多时候瓶颈根本不在服务器,而在你把一堆首屏用不上的东西,硬塞在了渲染的必经之路上。把路清干净,比把车换快,往往性价比高得多。

内联关键CSS有没有副作用?什么时候不该用?

关键CSS内联虽好,但不是万能药,有它的代价。最大的一个是缓存:内联在HTML里的CSS,没法像独立的 .css文件那样被浏览器长期缓存,用户每访问一个页面,这段关键CSS就得跟着HTML重新传一遍。对首次访问,这是稳赚;但对那种一个用户要连着翻很多页的站,重复传输的成本就得算一算了。

另一个代价是维护。关键CSS得跟着页面结构走,你改了首屏布局却忘了重新提取,内联的还是旧样式,轻则样式对不上,重则闪一下。所以我一般建议:把提取关键CSS这一步自动化进构建流程,别靠人肉维护。Google对优化CSS交付方式的建议里也是这个基调——首屏内联、其余延后,但得配好自动化,否则维护负担会反过来咬你。至于内联多少算合适,经验值是控制在14KB以内,正好卡在网络首个往返能塞下的数据量附近,超了收益就开始打折。

常见问题解答

关键CSS一定要手工提取吗?

不建议手工。小页面手工挑还行,站一大、页面模板一多就根本维护不过来。更靠谱的是用critical、penthouse这类工具自动提取,并接进构建流程,每次发布自动重新生成。这样样式改了,关键CSS也跟着更新,不会出现内联的是旧样式的尴尬。

内联关键CSS会不会让HTML文件变得很大?

会大一点,但可控。关键CSS一般就几KB到十几KB,经验上控制在14KB以内比较理想,正好贴着网络首个往返能传输的数据量。只要别把整个全站样式都塞进去,这点体积换来的首屏提速非常划算。真正要避免的是把非关键的样式也一股脑内联,那就本末倒置了。

async和defer到底怎么选?

看脚本依不依赖DOM、讲不讲顺序。相互独立、不碰页面结构的脚本(比如统计代码)用async,下载完立刻跑;依赖DOM或者彼此有先后关系的脚本用defer,等HTML解析完再按顺序执行。拿不准就用defer,它更稳妥,不会在解析中途突然打断。跟首屏无关的,能挪出head就挪出去。

关键渲染路径优化对SEO到底有没有帮助?

有,但是间接的。它主要改善FCP和LCP,而LCP是核心网页指标之一,属于排名参考信号。更重要的是它实打实提升了用户体验——首屏快,跳出就少、转化就好,这些行为信号长期看对排名也有正面作用。不过别指望它单独把排名拉起来,它是加分和保底,内容质量才是根本。还有个容易被忽略的间接好处:首屏渲染快、阻塞资源少,搜索引擎的爬虫渲染你的页面时也更省力、更不容易在渲染队列里排队超时,内容被完整抓取的概率更高。对重度依赖JS的站,这一点尤其值钱。

用了HTTP/2,还需要合并CSS、优化关键路径吗?

需要,只是重点变了。HTTP/2支持多路复用,多个小文件并行传输的成本比HTTP/1.1低了很多,所以过去那种把所有CSS合并成一个大文件的做法,现在不一定划算了。但关键渲染路径的核心矛盾没变——阻塞渲染的资源还是阻塞。首屏关键CSS内联、非关键异步、脚本defer,这些优化在HTTP/2下照样有效,甚至更容易做。

单页应用(SPA)也适用这套优化吗?

适用,而且往往更需要。SPA常常有一大坨JavaScript卡在关键路径上,首屏得等脚本下载、执行、渲染完才出来,白屏问题比传统站更严重。对它们来说,除了内联关键CSS,更关键的是服务端渲染或预渲染首屏、代码分割只加载首屏必需的JS。思路一致:把首屏必需的最快送达,其余的延后,别全堵在必经之路上。

权威参考资料

分享到
标签
版权声明

本文标题:《关键渲染路径怎么优化?阻塞渲染的CSS和JS拖慢首屏的机制与实战》

本文链接:https://zhangwenbao.com/critical-rendering-path-render-blocking-css-optimization.html

版权声明:本文原创,转载与引用请注明作者与原文链接。许可协议: CC BY 4.0

继续阅读
发表评论
分享到微信 或在下方手动填写
支持 Ctrl + Enter 提交