经济学家曼昆说:一个东西的成本是为了得到它而放弃的东西。
大家好,我是柒八九。
今天,我们继续前端面试的知识点。我们来谈谈关于浏览器的相关知识点和具体的算法。
该系列的文章,大部分都是前面文章的知识点汇总,如果想具体了解相关内容,请移步相关系列,进行探讨。
如果,想了解该系列的文章,可以参考我们已经发布的文章。如下是往期文章。
文章list
- CSS重点概念精讲
- JS_基础知识点精讲
- 网络通信_知识点精讲
- JS_手写实现
- 前端工程化_知识点精讲
- 前端框架_React知识点精讲
- React实战精讲(React_TS/API)
- Web性能优化_知识点精讲
- JS算法_知识点精讲
好了,天不早了,干点正事哇。
进程、线程
进程:某个应用程序的执行程序。
线程:常驻在进程内部并负责该进程部分功能的执行程序。
网页中的主要进程
渲染进程
Chrome
的默认策略是,每个标签对应一个Render Process
。- 它包含很多线程,这些线程一起负责将页面显示在屏幕上。例如:
- {合成线程|Compositor}
- {图块工作线程|Compositor Tile Worker}
- 主线程
进程复用
如果从一个页面打开了另一个新页面,而新页面和当前页面属于同一站点的话,那么新页面会复用父页面的渲染进程。官方把这个默认策略叫
process-per-site-instance
同一站点:根域名(wl.com
)加上协议(例如,https://
或者http://
),还包含了该根域名下的所有子域名和不同的端口
这三个域名就是同一站点。
GPU 进程
用于服务所有标签页和浏览器主进程的进程。
- 当页面数据(
frame
)被提交(commit
)到GPU进程时 - GPU进程继续对数据进行处理,
- 使其变成图块(
tiles
)和其他数据(DrawQuad
命令) - 并传输到系统
GPU
组件中的后缓冲区
- 提交完成之后,GPU 会将后缓冲区和前缓冲区互换位置,
- 也就是前缓冲区变成了后缓冲区,后缓冲区变成了前缓冲区
- 此时刚才提交的像素和图片就显示在浏览器上了
显示系统基础知识
基础概念
- 屏幕刷新频率:
- 一秒内屏幕刷新的次数(一秒内显示了多少帧的图像),
- 单位
Hz
(赫兹),如常见的60 Hz
。 - 刷新频率取决于硬件的固定参数(不会变的)。
- 逐行扫描:
- 显示器并不是一次性将画面显示到屏幕上,
- 而是从左到右边,从上到下逐行扫描,顺序显示整屏的一个个像素点,不过这一过程快到人眼无法察觉到变化。
- 以 60 Hz 刷新率的屏幕为例,这一过程即
1000 / 60 ≈ 16ms
。
- 帧率 (Frame Rate):
- 表示 GPU 在一秒内绘制操作的帧数,单位
fps
- 画面撕裂(tearing):
- 一个屏幕内的数据来自2个不同的帧,画面会出现撕裂感。
双缓存
画面撕裂原因
屏幕刷新频率是固定的,比如每16.6ms从buffer
取数据显示完一帧,理想情况下帧率和刷新频率保持一致,即每绘制完成一帧,显示器显示一帧。但是CPU/GPU
写数据是不可控的,所以会出现buffer
里有些数据根本没显示出来就被重写了,即buffer
里的数据可能是来自不同的帧的, 当屏幕刷新时,此时它并不知道buffer
的状态,因此从buffer
抓取的帧并不是完整的一帧画面,即出现画面撕裂。
双缓存
那咋解决画面撕裂呢?答案是使用 双缓存。
双缓存,让绘制和显示器拥有各自的buffer:GPU
始终将完成的一帧图像数据写入到 Back Buffer
,而显示器使用 Frame/Front Buffer
,当屏幕刷新时,Frame Buffer
并不会发生变化,当Back buffer
准备就绪后,它们才进行交换。
双缓存,CPU/GPU写数据到Back Buffer,显示器从Frame Buffer取数据
VSync(垂直同步信号)
问题又来了:什么时候进行两个buffer的交换呢?
当扫描完一个屏幕后,设备需要重新回到第一行以进入下一次的循环,此时有一段时间空隙,称为VerticalBlanking Interval(VBI)
。那,这个时间点就是我们进行缓冲区交换的最佳时间。因为此时屏幕没有在刷新,也就避免了交换过程中出现 screen tearing
的状况。
VSync
(垂直同步)是VerticalSynchronization
的简写,它利用VBI
时期出现的vertical sync pulse
(垂直同步脉冲)来保证双缓冲在最佳时间点才进行交换。另外,交换是指各自的内存地址,可以认为该操作是瞬间完成。
渲染进程主线程
- 页面渲染起始标识
- 当垂直同步信号(VSync)被排版线程接收到,新的屏幕渲染开始
- 输入事件回调
- 输入事件的数据信息从排版线程向主线程的事件回调中传递。
- 所有输入事件的回调(
touchmove/scroll/click
)应该先被调用,并且每帧都应该触发,但是这不是必须的
- rAF(
requestAnimationFrame
)
- 这是一个用于屏幕视觉更新的理想的位置。
- 因为,在此处能够获取到垂直同步事件最新的输入数据。
- {解析HTML|Parse HTML}
- 通过指定的解析器,将不能被浏览器识别的HTML文本,转换为浏览器能识别的数据结构:DOM对象。
- DOM本质上是一种接口(API),是专门操作网页内容的API标准。
- 重新计算样式
- 对新生成或被修改的元素进行样式信息计算。
- 此过程可能触发整个DOM树的整体计算也可以是局部小范围的计算过程,取决于被改动的元素的位置。
- 例如,改动
body
元素的属性,就会发生整个DOM树的重新计算。
- 将元素样式和DOM元素结合起来,就会生成
Render Tree
- {布局|Layout}
- 计算每个可视元素的位置信息(距离视口的距离和元素本身大小)。
- 并生成对应的
Layout Tree
。
- {更新图层树|Update Layer Tree}
- 在 Render 树的基础上,我们会将拥有相同z 坐标空间的
Layout Objects
归属到同一个{渲染层|Paint Layer}中。 Paint Layer
最初是用来实现{层叠上下文|Stacking Context}
- 它主要来保证⻚面元素以正确的顺序合成。
- {绘制|Paint}:
- 该过程包含两个过程,
- 第一个过程是绘制操作(painting)
- 该过程用于生成任何被新生成或者改动元素的绘制信息(包含图形信息和文本信息);
- 第二个过程是栅格化(Rasterization),
- 用于执行上一个过程生成的绘制信息。
- {页面合成|Composite}:
- 将图层信息(layer)和图块信息提交(commit)到合成线程中。并且在合成线程中会对一些额外的属性进行解释处理。
- 例如:某些元素被赋值
will-change
或者一些使用了硬件加速的绘制方式(canvas
)。
- {栅格化|Rasterize}:
- 在绘制阶段(Paint)生成的绘制记录(Paint Record)被合成线程维护的{图块工作线程|Compositor Tile Worker}所消费。
- 栅格化是根据图层来完成的,而每个图层由多个图块组成。
- 页面信息提交:
- 当页面中所有的图层都被栅格化,并且所有的图块都被提交到{合成线程|Compositor},此时{合成线程|Compositor}将这些信息连同输入数据(input data)一起打包,并发送到GPU线程。
- 页面显示:
- 当前页面的所有信息在GPU中被处理,GPU会将页面信息传入到双缓存中的后缓存区,以备下次垂直同步信号到达后,前后缓存区相互置换。然后,此时屏幕中就会显示想要显示的页面信息。
额外的奖赏
- requestIdleCallback:如果在当前屏幕刷新过程中,主线程在处理完上述过程后还有剩余时间(<16.6ms),此时主线程会主动触发
requestIdleCallback
。
客户端缓存
本地存储小容量
Cookie
主要用于用户信息的存储,Cookie的内容可以自动在请求的时候被传递给服务器。
- 服务器在响应 HTTP 请求时,通过发送
Set-Cookie
HTTP 头部包含会话信息。 - 浏览器会存储这些会话信息,并在之后的每个请求中都会通过 HTTP 头部 cookie 再将它们发回服务器。
- 有一种叫作
HTTP-only
的cookie
。HTTP-only
可以在浏览器设置,也可以在服务器设置,但只能在服务器上读取
Web Storage
- 提供在
cookie
之外的存储会话数据的途径 - 提供跨会话持久化存储大量数据的机制
Web Storage
的第 2 版定义了两个对象:
LocalStorage
的数据将一直保存在浏览器内,直到用户清除浏览器缓存数据为止。SessionStorage
的其他属性同LocalStorage
,只不过它的生命周期同标签页的生命周期,当标签页被关闭时,SessionStorage
也会被清除。 。
本地存储大容量
IndexDB
:是浏览器中存储结构化数据的一个方案
IndexedDB
是类似于MySQL
或Web SQL Database
的数据库
WebSQL
: 用于存储较大量数据的缓存机制。
- 已废弃并且被
IndexDB
所替代
Application Cache
:允许浏览器通过manifest
配置文件在本地有选择的存储JS/CSS/图片等静态资源的文件级缓存机制
- 已废弃并且被
ServerWorkers
所替代
ServerWorkers
硬件加速
初次渲染时会经过以下几步
- 构建DOM树;
- 样式计算;
- 布局定位; Layout Tree
- 图层分层; Layer Tree
- 图层绘制;
- 合成显示;
在CSS属性改变时,重渲染会分为回流、重绘和直接合成三种情况,分别对应从“布局定位”/“图层绘制”/“合成显示”开始,再走一遍上面的流程。
元素的CSS具体发生什么改变,则决定属于上面哪种情况:
- 回流(又叫重排):元素位置、大小发生变化导致其他节点联动,需要重新计算布局;
- 重绘:修改了一些不影响布局的属性,比如颜色;
- 直接合成:合成层的
transform
、opacity
修改,只需要将多个图层再次合并,而后生成位图,最终展示到屏幕上;
渲染层
拥有z-index
属性的定位元素会生成一个层叠上下文,一个生成层叠上下文的元素就生成了一个渲染层。
形成渲染层的条件也就是形成层叠上下文的条件,有这几种情况:
- 天生派
- 页面根元素天生具有层叠上下文
- 根层叠上下文
- 正统派
z-index
值为数值的定位元素的传统层叠上下文
- 扩招派 (CSS3属性)
- 元素为
flex
布局元素(父元素display:flex|inline-flex
),同时z-index
值不是auto - flex布局 - 元素的
opactity
值不是1 - {透明度|opactity} - 元素的
transform
值不是none
- {转换|transform} - 元素
mix-blend-mode
值不是normal
- {混合模式|mix-blend-mode} - 元素的
filter
值不是none
- {滤镜|filter} - 元素的
isolation
值是isolate
- {隔离|isolation} - 元素的
will-change
属性值为上面②~⑥的任意一个(如will-change:opacity
) - 元素的
-webkit-overflow-scrolling
设为touch
合成层
只有一些特殊的渲染层才会被提升为合成层,通常来说有这些情况:
transform:3D变换
:translate3d
,translateZ
;will-change
:opacity
|transform
|filter
- 对
opacity
|transform
|fliter
应用了过渡和动画(transition/animation) video
、canvas
、iframe
上面这些条件属于生成渲染层的“加强版”,也就是说形成合成层的条件要更苛刻。
硬件加速
给HTML元素加上某些CSS属性,比如3D变换,将其提升成一个合成层,独立渲染。
之所以叫硬件加速,就是因为合成层会交给GPU(显卡)去处理,在硬件层面上开外挂,比在主线程(CPU)上效率更高。
后记
分享是一种态度。
全文完,既然看到这里了,如果觉得不错,随手点个赞和“在看”吧。