tomoya06 / web-developer-guidance

Actually it's just a notebook for keeping down some working experience.
4 stars 0 forks source link

JavaScript - 浏览器渲染机制 #13

Open tomoya06 opened 4 years ago

tomoya06 commented 4 years ago

渲染机制

渲染过程

  1. 处理 HTML 并构建 DOM(Document Object Model) 树。
  2. 处理 CSS 构建 CSSOM 树。
  3. 将 DOM 与 CSSOM 合并成一个渲染树。
  4. 根据渲染树来布局,计算每个节点的位置。
  5. 调用 GPU 绘制,合成图层,显示在屏幕上。

如果 DOM 或 CSSOM 被修改,以上过程需要重复执行,这样才能计算出哪些像素需要在屏幕上进行重新渲染。

image

多线程

浏览器渲染进程是多线程模型,主要包含了如下一些线程类型:

  1. GUI渲染线程:解析HTML,CSS,构建DOM树、构建render树,布局和绘制等
  2. JS引擎线程:处理JavaScript程序
  3. 事件触发线程:当JS引擎执行如setTimeOut代码块时,或者是其他的如网络异步请求、鼠标点击等时,会将对应任务添加到事件线程中。当对应的事件符合触发条件被触发时,该线程会把事件添加到待处理的事件队列中,等待JS引擎空闲时处理。
  4. 定时器线程:setInterval与setTimeout所在线程。计时完毕后,将对应事件添加到事件队列中,等待JS引擎空闲时处理。
  5. 异步网络请求线程:处理网络请求,如果设置有回调函数,异步线程就产生状态变更事件,将这个回调再放入事件队列中。等待JS引擎空闲时处理。

GUI渲染线程和JS引擎线程是互斥的,当JS引擎线程执行时,GUI渲染线程会被挂起。

加载完成事件

参考这篇博客

事件触发流程

参考自己实现代码 另外有看到document.onload事件,测试时发现这个方法已经失效。参考栈溢出网友的回答,window.onload在整个页面加载完成时触发,document.onload在DOM树构建好的时候触发。不过现在的浏览器都默认用window.onload直接替代document.onload事件。

阻塞环节

改变script阻塞

defer和async可以改变script标签阻塞的情况。

首先,注意 async 与 defer 属性对于 inline-script 都是无效的,还是会按顺序执行。主要看带src属性的script标签。

defer

<script defer src="script.js"></script>

defer 表示延迟执行。与相比普通 script,有两点区别:

整个 document 解析完毕且 defer-script 也加载完成之后(这两件事情的顺序无关),会执行所有由 defer-script 加载的 JavaScript 代码,然后再触发 DOMContentLoaded 事件。

async

<script async src="script.js"></script>

async 表示同步执行。如果已经加载好,就会开始执行,注意这种方式加载的 JavaScript 依然会阻塞 load 事件。

动态创建sciprt标签

使用 document.createElement 创建的 script 默认是异步的,不会阻塞。

图层

一般来说,可以把普通文档流看成一个图层。特定的属性可以生成一个新的图层。不同的图层渲染互不影响,所以对于某些频繁需要渲染的建议单独生成一个新图层,提高性能。

通过以下几个常用属性可以生成新图层

重绘(Repaint)和回流(Reflow)

重绘和回流是渲染步骤中的一小节,但是这两个步骤对于性能影响很大。

回流必定会发生重绘,重绘不一定会引发回流。回流所需的成本比重绘高的多,改变深层次的节点很可能导致父节点的一系列回流。

引起重绘的属性和方法

image

引起回流的属性和方法

如何减少回流重绘