rax-app icon indicating copy to clipboard operation
rax-app copied to clipboard

[RFC] 支持 CriticalCSS

Open fengzilong opened this issue 4 years ago • 5 comments

背景

Critical CSS 可以显著提升 FCP 和 Speed Index 的数据,实测使用后 FCP 从 1.8 -> 0.9s,原因是消除了首屏 css 对首屏渲染的阻塞,目前 rax 没有关于 Critical CSS 的官方最佳实践

关于 Critical CSS

可以看到,外链的 CSS 文件加载会阻塞页面的渲染

而将首屏用到的样式使用 <style></style> 标签内联至 head,同时将 <link rel="stylesheet" /> 标记为非阻塞的资源(或者挪到 <Root /> 后面),可以让页面提前开始渲染,可以节省中间 CSS 文件下载和解析导致的阻塞时间(这个和 SSR vs CSR 是类似的,SSR 也是节省了客户端下载和执行 JS 的时间,让首屏提前渲染)

同时这个方案还有另一个优点

如果我们对 CSS 文件进行覆盖率收集,会发现首屏往往只用到了其中 50% 左右的样式,剩余 50% 都不是首屏需要用到的(这存在额外的下载和解析开销),Critical CSS 方案可以只提取首屏用到的样式,可以避免多余的下载和解析开销

图片来自:Defer non-critical CSS

如何使用

import { CriticalCSS } from 'rax-document'

export default () => {
  return <html>
	<head>
	<meta ...>
	        
	{ /* 自定义 CriticalCSS 的位置,这里实际渲染出来大概是这样: <style>.header {...}</style> */ }
	<CriticalCSS />
	  
	</head>
	<body>
	  <Root />
	  <Data />
      
      { /* 这里特地把 Style 放到了 Root 后面,避免阻塞首屏渲染 */ }
	  <Style />
	  <Script />
	</body>
  </html>
}

实现思路

从 initialHtml 中提取出所有的 class,再去页面对应的 css 文件中捞出对应的样式,作为 <style></style> 的 children

fengzilong avatar Sep 12 '21 14:09 fengzilong

dynamic import 是不是可以满足这个需求?

SoloJiang avatar Sep 13 '21 07:09 SoloJiang

dynamic import 是不是可以满足这个需求?

应该解决不了,critical css 的核心是要把 <link rel="stylesheet" /> 换成 <style></style>,而且有些组件即使是首屏的,但是需要根据链接参数决定是否展示,也无法简单地归类到 dynamic import 里面

fengzilong avatar Sep 13 '21 16:09 fengzilong

可以提一个较详细 RFC

SoloJiang avatar Sep 14 '21 02:09 SoloJiang

可以提一个较详细 RFC

已更新,稍微补充了下,核心部分已经实现好了,可以直接来个 PR

fengzilong avatar Sep 14 '21 07:09 fengzilong

可以补充下,无 document 模式应该怎么使用

SoloJiang avatar Sep 15 '21 08:09 SoloJiang