浏览器既是搬运工,也是翻译官。
- 将枯燥的网络资源(html css js)从服务器“搬”到本地
- 将资源“翻译”成五彩的页面
- 接受并反馈用户的交互
渲染引擎
翻译的工作,最主要是由,浏览器的渲染引擎做的。
翻译的过程如下:
- 访问网址的时候,服务器一般会首先返回 html 文件
- 渲染引擎,开始解析
html文件,生成DOM树。在这过程中遇到其他资源(css img js),会发出新的请求 - 识别并加载所有的
CSS样式信息,生成CSSOM树,注意这里的样式会添加到DOM树的各个节点 - 将
DOM树和CSSOM树合并,将不可见的元素剔除,生成render树(:after :before 伪元素会在此时被构建到 DOM 树中) - 计算页面中所有元素的相对位置、大小等信息,就有了
盒模型 - 开始把每一个页面图层转换为像素,并对所有的媒体文件进行解码,然后五彩的页面就显示出来了
总结下:
DOM树 =>CSSOM树 =>render树 => 布局 => 绘制
重绘、重排、图层
页面不会一成不变的,JS 可以随意操作 DOM,从而会让渲染引擎再次翻译。
不同的操作,翻译成本不同,主要分为两种操作:
- 重排(回流):操作引发了 DOM 几何尺寸的变化(修改元素的宽高或隐藏元素等)时,于是需要重新计算元素 的几何属性(其他元素的几何属性和位置也会因此受到影响),然后再绘制出来。
对应的流程:可能更新
DOM树 => 更新CSSOM树 => 更新render树 => 重新布局 => 重新绘制 - 重绘:操作只修改了未影响元素几何属性的样式(比如修改了颜色或背景色)时,就会跳过计算、直接绘制。
对应的流程: 更新
CSSOM树 => 更新render树 => 重新绘制
引发重排的操作:
- 修改元素的几何属性
- 改变 DOM 的结构,增减、移动等
- 获取一些特定属性的值:offsetTop|Left|Width|Height、scrollTop|Left|Width|Height、 clientTop|Left|Width|Height,这些属性均通过即时计算得到,于是浏览器为了获取这些值,也会进行回流
重排的代价很昂贵,尽量减少重排的操作,频繁引发重排,会让页面卡顿滴~
事件循环机制 event-loop
以上还没提到 JS 文件的翻译,JS 是由 JS 引擎翻译的。
先提一些名词:
- 宏任务(macro-task)队列:script(整体代码)、setTimeout、setInterval 等
- 微任务(microtask) 队列:Promise 等
- 函数调用栈
JS 引擎从开始执行 Script 脚本的时候,这本身就是一个宏任务,然后创建全局上下文,进栈
- 如果遇到
Promise的then代码,不会直接执行,会在此宏任务的内部,开启一个微任务队列,这个then任务就是排队的第一人 - 如果遇到
setTimeout/setInterval,不会直接执行,会另外开启一个宏任务,排在当前宏任务的后面,如果这个setTimeout里面再遇到then,也会在它的内部开启一个微任务队列 - 如果当前的宏任务的同步代码执行完了,会依次执行当前宏任务内部的,微任务队列里的任务
- 如果当前的宏任务的同步代码执行完了,微任务队列里的任务也执行完了,就会执行下一个宏任务
- 函数调用栈,每次执行一个函数,就创建了函数上下文,进栈,执行完,出栈,没啥说的,之前说过了
function app() {
// s-1
setTimeout(() => {
console.log("1-1");
// p-1
Promise.resolve().then(() => {
console.log("2-1");
});
});
console.log("1-2");
// p-2
Promise.resolve().then(() => {
console.log("1-3");
// s-2
setTimeout(() => {
console.log("3-1");
});
});
}
app();
// 1-2 1-3 1-1 2-1 3-1
画了个图,希望能帮助读者理解: