白屏时间如何界定
用户输入地址,到看到内容的这个时间内,页面存在的白屏时间。
浏览器做了以下操作:
- dns解析:将域名解析为ip地址;
- 建立tcp链接:三次握手;
- 发起http请求:浏览器向服务器发送请求;
- 服务器响应;
- 浏览器解析html生成dom树;
- 浏览器解析css生成cssom,并结合dom树生成布局树,从而实际进行渲染;
- 页面进行展示
根据操作可以归纳出以下的影响要素
- 网速、网络稳定性:网络延迟、带宽、dns解析时长等等。
- 服务器性能:提高服务器响应速度,这个是后端考虑的范畴了
- 前端页面结构:HTML文档的大小、复杂度、外部资源的加载顺序等都会影响白屏时间。如果HTML文档过大或包含大量外部资源,浏览器需要更长的时间来解析和渲染页面。
- 浏览器性能:浏览器的渲染引擎性能、缓存机制等也会影响白屏时间。不同浏览器的渲染性能存在差异,导致白屏时间不同。
对策
-
开启dns预解析(dns-prefetch),站点进行cdn部署
-
开启http缓存,结合构建工具合理配置分包策略,充分利用浏览器缓存
-
开启脚本的异步加载,async、defer方案。
a. async标签的script,浏览器遇见会立即加载立即运行,会阻塞html解析,不保证执行顺序
b. defer标签的script,浏览器立即立即加载但延迟运行,不会阻塞解析,保证执行顺序
c. 指定rel="prefecth"的link,浏览器空闲时拉取并存入缓存,且不阻塞正常解析,,不保证资源之间的执行顺序。
d. 指定rel="preload"的link,希望浏览器尽早的请求资源,且不阻塞正常的onload,不保证资源之间的执行顺序。
preload必须设置as属性来声明资源的类型(font/image/style/script等),否则浏览器可能无法正确加载资源。对于字体文件或者可以加载的跨域资源需要加上crossorigin属性。
如何计算白屏时长
- 手动计算
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>白屏时间计算</title>
<script>
// 记录页面开始加载的时间
window.pageStartTime = Date.now();
</script>
<link rel="stylesheet" href="https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-assets/ionicons/2.0.1/css/ionicons.min.css~tplv-t2oaga2asx-image.image">
<link rel="stylesheet" href="https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-assets/asset/fw-icon/1.0.9/iconfont.css~tplv-t2oaga2asx-image.image">
<script>
// head 解析完成后,记录时间
window.firstPaint = Date.now();
console.log(`白屏时间:${firstPaint - pageStartTime}ms`);
</script>
</head>
<body>
<div class="container"></div>
</body>
</html>
- Performance Api
// 性能 观察器 观察者模式
const observer = new PerformanceObserver((list) => {
// 获取所有的 性能 指标
const entries = list.getEntries();
for(const entry of entries) {
// body 里的第一个 标签的渲染
// 'first-paint' 表示页面首次开始绘制的时间点,也就是白屏结束的时间点
if(entry.name === 'first-paint') {
const whiteScreenTime = entry.startTime;
console.log(`白屏时间:${whiteScreenTime}ms`);
}
}
})
// 首次绘制 first-paint
// 首次内容绘制 first-contentful-paint 事件
// observe 监听性能指标
// buffered 属性设置为 true,表示包含性能时间线缓冲区中已经记录的相关事件
// 这样即使在创建 PerformanceObserver 之前事件已经发生,也能被捕获到
observer.observe({ type: 'paint', buffered: true });