本文以“当浏览器输入URL之后发生了什么”为主线系统阐述浏览器解析和渲染的过程,当然有一些知识点如DNS网络协议等,会提一嘴但不属于本文范畴不会深入讲解,此外本文还会介绍网页性能指标相关
用户发出URL请求到页面开始解析的过程叫做导航
红色为第一次请求(主线)
完成的渲染流程示意图
具体流程细节如下
大致总结:
HTML 与DOM 的关系
DOM的作用
思考: HTML的解析是等待整个HTML加载完成之后开始解析,还是随着HTML文档边加载边解析?
答案 肯定是后者,因为效率高,具体流程如下
因为JS文件的下载过程会阻塞DOM解析,而通常下载又是非常耗时的,会受到网络环境、JavaScript 文件大小等因素的影响。
不过 Chrome 浏览器做了很多优化,其中一个主要的优化是预解析操作。当渲染引擎收到字节流之后,会开启一个预解析线程,用来分析 HTML 文件中包含的 JavaScript、CSS 等 相关文件,解析到相关文件之后,预解析线程会提前下载这些文件。
开发中我们可以通过一些方案来规避
<link>
标签会异步下载并继续解析HTML补充知识点:
很多图形操作都很复杂需要大量的运算,比如一副完整的画面,可能需要很多次计算才能完成,如果每次计算完成一部分图像,就将其写入缓冲区,那么会造成一个后果就是现实一个稍微复杂的图像过程中,你看到的页面效果可能是一部分一部分地显示出来,因此刷新页面的过程中,会让用户感觉到界面的闪烁
而使用双缓存,可以让你将计算的中间结果放在另一个缓冲区,等全部计算技术,该缓存区已经存储了完整的图形之后,再将该缓冲区的图像数据一次性复制到缓冲区,这样整个图像的输出非常稳定。
虚拟DOM与真实DOM 真实DOM的问题:因为JS是单线程,DOM操作与JS执行互斥,如果JS对DOM的操作不当可能引发强制同步布局和布局抖动的问题,这些操作会大大降低渲染效率。 在这里你可以类比,把虚拟DOM堪称DOM的一个buffer,和图形显示一样,它会在完成一次完整的操作之后,再把结果应用到DOM上,这样就能减少一些不必要的更新,同时能保证DOM的稳定输出
chrome中的合成技术可以用三个词来概括总结:分层、分块和合成
为什么要引入分层合成机制?
分层合成机制的实现
分块
如何分层优化代码
will-change
告诉显然引擎你会对该元素做一些特效变换will-change: transform;
CSS
动画比JavaScript
动画高效的原因(1)前面的渲染流程是指首屏渲染的过程,然而渲染渲染第二帧及后续帧则不需要那么麻烦。一般经历重排或重绘或合成三个过程之一即可
如果改变了元素的几何属性,例如盒模型相关,浏览器会触发重新布局,解析之后的一些列子阶段。也叫回流
触发页面重布局的属性
width
height
padding
margin
border-width
border
min-height
display
top
bottom
left
right
position
float
clear
text-align
font-weight
overflow
font-family
line-height
vertival-align
white-space
font-size
当render tree
中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的,比如background-color
。则就叫称为重绘。
相对于重排:重绘省去了布局和分层阶段,所以执行效率要比重排操作要高一些
只触发重绘的属性
color
border-style
border-radius
text-decoration
visibility
background
background-image
background-position
background-repeat
background-size
outline-color
outline
outline-style
outline-width
box-shadow
假如我们使用了CSS的transform来实现动画效果,这可以避开重排和重绘,合成能大大提升绘制效率
完整的一帧
requestAnimationFrame
在渲染一帧前如果有空闲时间则执行它requestIdelCallback
在每一帧渲染后,下一帧绘制之前如果有空闲时间,则调用之像素管道
window.performance.timing
navigationStart
触发跳转的时间,如果没有前一个页面则是从fetchStart开始unloadEventStart
/ unloadEventEnd
前一个页面卸载的时间戳,无上一个页面,默认0redirectStart
/ redirectEnd
如果没有重定向两个值都是0worker
初始化事件 + 加载事件的时间fetchStart
页面开始的事件DomainLookupStart
/ DomainLookupEnd
DNS 递归解析 迭代查询的时间, 这里会拿到 domain 对应的 ipconnectStart
/ connectEnd
等待TCP队列及三次握手时间
a. chrome有个机制,同一个域名,同时最多只能建立6个TCP连接,剩下的请求会进入排队等待状态secureConnectionStart
建立https连接的时间requestStart
/ responseEnd
请求时间domLoading
开始加载DOMdomInteractive
只是完成dom的解析,并没有开始加载网络资源domContentLoadedEventStart
/ domContentLoadedEventEnd
开始解析DOM树事件 readyStateChange
domComplete
DOM树readyloadEventStart
/ loadEventEnd
执行脚本开始与结束上述指标到 domComplete
只是index.html
加载完成的首次渲染,对于SPA
应用来说还基本处于白屏状态,衡量网站性能还需要更多指标
Core Web Vitals
(网页核心性能指标)是 Google(谷歌)认为在网页的整体用户体验中很重要的一组特定因素。它是由三个特定的页面速度和用户交互测量值组成:链路控制协议、网页交互性和可视元素偏移。
LCP(Largest Contentfull Paint)最大内容渲染时间 衡量页面装载的性能,页面加载前2.5s内必须要进行最大内容渲染 最大内容包括
<image>
<svg>
<video>
url
加载内容的模块LCP值低的原因
FID(First Input Delay) 衡量交互体验 页面首次输入延迟小于100ms
DOM
渲染javascript// main.js
const worker = new Worker('./worker.js');
worker.postMessage(' Come on & work~');
worker.onmessage = function(e) {
console.log(e.data);
}
// worker.js
self.onmessage = function(e) {
console.log(e.data);
self.postMessage('干完了,下班了')
}
javascript// main.js
navigator.serviceWorker.register('service-worker.js');
// service-worker.js
self.addEventListener('install', fn)
self.addEventListener('active', fn)
self.addEventListener('fetch', (e) => {
e.respondwith(catchs.match(event.request))
})
javascript// worklet 俗称 JS in CSS
/* main.js */
CSS.paintWorklet.addModule('worklet.js');
/* worklet.js */
registerPaint('myGradient', class {
paint(ctx, size, prop) {
var gradient = ctx.createLinearGradient(0, 0, 0, size.height - 5);
gradient.addColorStop(0, "black");
gradient.addColorStop(1, "white");
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, size.width, size.height);
}
})
/* app */
.content {
background-image: paint(myGradient);
}
CLS (Cumulative Layout Shift)
累计布局偏移 - 衡量视觉稳定性
页面要保持CLS小于0.1 => 可见元素从前一帧到后一帧改变位置的动作
避免使用无尺寸元素
html<img
srcset="a-320w.jpg 320w,a-480w.jpg 480w, a-800w.jpg 800w,"
sizes="(max-width: 320px) 300px, (max-width: 480px) 440px, 800px">
chrome应用商店有个CWV工具 Core Web Vitals Annotations
chrome devtools performance
js可以通过PerformanceObserver
获取这些指标
js// 创建一个新的 PerformanceObserver 实例
let observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// 处理每个性能条目
console.log(entry);
}
});
// 开始观察特定类型的性能条目
observer.observe({ entryTypes: ['paint', 'mark', 'measure'] });
// 在不需要时,停止观察
// observer.disconnect();
衡量性能的工具
硬件加速又称GPU加速, 以来浏览器渲染时使用的分层模型
animation
、transform
和transition
不会自动进行GPU加速opacity
translateZ()
translate3d()
会开启硬件加速注意 GPU加速也有缺点
will-change
属性允许你提前告知浏览器你可能会对一个元素进行什么样的改变,这样浏览器就可以提前设置适当的优化will-change
属性并不会立即触发元素的重绘或重排will-change
属性有以下几种使用方式:
指定要改变的属性:will-change: property;
,其中 property 是要改变的 CSS 属性的名称,比如 transform
、opacity
、scroll-position
等。
指定多个属性:will-change: property1, property2, ...;
,可以同时指定多个属性,以逗号分隔。
全局优化提示:will-change: auto;
,表示元素可能会有任何属性的改变。
注意事项
will-change
不要滥用,浏览器进行优化准备也有成本will-change
,几乎没有任何效果will-change
本段落参考资料
- 浏览器已经尽可能的在页面下做了最大的优化,但每个浏览器引擎的实现方法并不尽相同。
- 而 contain 属性可以提供一种标准的方式让开发人员告诉 浏览器 某些方面可以这样优化,哪些不能优化。
contain
保证了它和它的子元素的DOM变化不会触发父元素的重新布局、渲染等。contain 属性值有7中
none
无layout
告知浏览器此元素及子元素不会在影响外部元素的布局style
官方说辞是因为存在某些风险,暂时被移除,可能在规范的第二版会重新定义吧paint
元素的子元素不会在此元素的边界之外被展示size
元素的渲染不会受到其子元素内容的影响。content
相当于 contain: size layout paint
strict
相当于 contain: layout paint
本段参考资料
本文作者:郭敬文
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!