配置关键 CSS
这个模块做什么——以及不做什么
Kakapo 会为每个页面模板通过 HTTP 取回你站点上一个真实的示例页面(只在后台管理区或由 Cron 触发,绝不在访客的页面构建过程中),直接从硬盘读取那里链接的本地样式表,并用 DOMXPath 把每一条规则逐一与该文档比对。由此产生两个结果:会被内联放进 head 的关键 CSS,以及在这个模板上一个元素都没命中的规则清单。
整个过程不做任何渲染。没有浏览器,没有视口,也没有滚动位置。“首屏可见”是按一条公开规则得出的近似值:body 之下的所有元素被依次编号,前 30%(默认值,可在 5 到 90 之间设置)算作首屏,再加上已登记的地标之内的一切。始终会被收进来的有 html、body、:root 和 @font-face,以及被某条关键规则通过 animation 指名的 @keyframes。只在特定状态下才生效的规则(:hover、:focus、:active)留在外面,@media print 同样如此。当这套办法触及自身边界时,它会往上取整:该规则算作命中。多出几 KB 的代价,小于少了一条规则和随之而来的闪烁。
分析之前先检查先决条件
有两件事必须成立。第一是 PHP 扩展 dom/libxml——本模块不是用 class_exists() 来检查它,而是构建一个微型文档并真的去查询它。原因在于:有些主机商设置了 disable_classes=DOMDocument,这时 class_exists() 仍旧报告“是”,但 new 会失败。如果缺少这个扩展,“分析”和“每天刷新”会被锁定,旁边写着原因。
其次是 Loopback:站点必须能通过 HTTP 访问到自己。为此在“先决条件”区域里有一个“检查 Loopback”按钮;它会记下 HTTP 状态码、响应大小和时长。如果它失败,每一次分析运行都会失败——常见原因是站点前面有防火墙、预发布环境环境上有 Basic-Auth 保护,或者主机名在内部无法解析。
分析读不到的东西也不会被延迟加载:来自外部主机、来自 CDN 或来自 Google Fonts 的样式表依旧会阻塞渲染。这是慢一些、但稳妥的做法。
一个模板一个模板地推进
一共有七个模板:首页、博客页、单篇文章、静态页面、归档、搜索结果和 404 错误页。每一个都单独分析、单独放行。如果你的首页显示的就是文章列表,那么“博客页”不适用——开关会保持锁定,因为它不会有任何作用。如果没有已发布的文章或没有页面,就缺少示例页面,该模板同样保持锁定。
推荐的做法是:先“全部分析”或逐个分析。之后针对某个模板在两个标签页里分别打开“预览”和“不处理”——这是同一个真实页面,一次经过处理,一次未经处理,只有已登录的管理员、且地址中带有有效的一次性密钥时才能看到。为此主开关并不需要打开。页面构建时的闪烁,最可靠的观察方式是在单独的标签页里限速加载,而不是在内嵌的框架中。
只有当对比看起来干净利落时,你才放行这个模板并打开主开关。它一开始处于测试运行:这时只有已登录的管理员能看到处理效果,对其他所有人页面都保持原样。等你把多个模板都检查过之后,再切到“正式启用”。
四道刹车和例外清单
“延迟加载已分析的样式表”才是真正省时间的地方。关闭表示:关键 CSS 只是额外内联进来,其余一切照旧——没有风险,但也没有收益。开启表示:被解析的样式表通过 media="print" 加上 onload 复位来加载,并为没有 JavaScript 的访客附上一份 noscript 副本。被延迟的只有真正读取过的内容,而且必须句柄和文件地址仍然与分析时的一致。
“源文件变化时暂停”会为每个被解析的文件记住修改时间和大小,以及当前启用的主题。一旦有变化,就干脆不做处理,而不是用可能已经错误的关键 CSS——这时源代码里会有一条写明原因的说明。
三个文本框是紧急出口。排除 URL:每行一个路径,“*”作通配符,这些页面永远不会被处理(典型的如 /checkout*、/mein-konto*)。永不延迟的样式表:每行一个 WordPress 句柄,默认是 admin-bar 和 dashicons。地标:每行一个选择器,其中的一切都算作首屏——默认是 header、[role="banner"]、.site-header、#masthead、nav、.main-navigation、.site-branding、.hero 和 h1。无法解析的行会被拒绝,而不是悄悄保存下来。
修改“首屏”比例或地标之后,要等到重新跑一次分析才会生效。其他所有改动都会立即作废页面缓存,因为那里存放的是带着旧状态的成品 HTML。
如果出了问题
“时间预算已用尽”:本次运行达到了 25 秒或 PHP 的时间上限。报告已经保存,关键 CSS 则有意没有保存——不完整的关键 CSS 会导致闪烁。提示中会写明是在哪个文件处结束的。请重新分析,或者通过例外清单把某个特别大的样式表排除出去。
“返回了 HTTP …”:示例页面没有给出预期的状态码(200,错误页面则是 404)。不会保存任何内容。请检查该页面在浏览器中是否可以访问,前面是否挡着某种防护。
某个模板显示为“已过时”:某个被解析过的 CSS 文件发生了变化,或者主题被更换了。重新分析即可。如果那里写着“比例不同”,说明该模板是用与当前设置不同的百分比生成的——同样重新分析。
为了不让这件事停留在手工操作上,还有“每天检查并刷新”。这个 Cron 运行只分析已经放行、且已过时的模板;它从不启用任何东西,也不会为未放行的模板建立分析。它最近一次的结果会作为说明写在“安全带”区域里。
如果某个页面依然不安稳,看一眼源代码会有帮助:那里有一条注释,写着被处理的模板和状态(预览、测试运行、正式启用),或者写着为什么有意不做处理。