1. 浏览器整体架构

现代浏览器不是一个单线程程序,而是多进程、多线程协作。

简化模型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Chromium

├── Browser Process
│
│ 管理浏览器本身:
│ - 标签页
│ - 窗口
│ - 导航
│ - 权限
│
├── Network Service
│
│ 提供网络能力:
│ - DNS
│ - TCP
│ - TLS
│ - HTTP
│ - Socket
│
├── GPU Process
│
│ 负责 GPU 相关工作
│
└── Renderer Process

负责网页运行

├── Runtime
├── Blink
├── V8
└── Rendering Pipeline

2. Renderer Process 内部模型

Renderer 是网页真正运行的位置。

可以理解:

1
2
3
4
5
6
7
8
9
10
11
12
13
Renderer Process


Runtime

|
+------+------+
| |
↓ ↓

Blink V8

Web平台能力 JavaScript执行

Runtime 是什么?

Runtime 可以理解为:

网页运行时的协调系统。

它负责:

  • 调度任务
  • 管理事件循环
  • 协调 Blink 和 V8
  • 控制什么时候执行 JavaScript
  • 控制什么时候进入渲染流程

它不是:

  • HTML解析器
  • JS执行器
  • DOM实现

而是:

控制这些模块如何协作。


3. Blink 和 V8 的职责

V8

负责:

JavaScript语言本身。

例如:

1
2
3
4
5
Promise.resolve()

Array.map()

let a = 1

这些属于 ECMAScript。

流程:

1
2
3
4
5
6
7
8
9
JavaScript

↓

V8

↓

执行

负责:

浏览器提供的 Web 平台能力。

例如:

1
2
3
4
5
6
7
8
9
document.querySelector()

fetch()

setTimeout()

EventSource()

WebSocket()

这些不是 JavaScript 自带。

而是浏览器提供。


4. Binding(绑定层)

Binding 是:

JavaScript 世界和浏览器 C++ 实现之间的桥梁。

例如:

1
document.querySelector("#app")

流程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
JavaScript

↓

V8执行调用

↓

Binding

↓

Blink C++实现

↓

DOM查询

↓

返回结果

↓

Binding包装

↓

V8返回JS对象

所以:

V8 不懂 DOM。

V8 只知道:

调用了一个函数。

真正修改 DOM:

是 Blink。


5. Web API 和 JavaScript API 区别

判断方法:

问:

这个能力是不是 ECMAScript 标准?


JavaScript 自带

例如:

1
2
3
4
5
6
Promise
Array
Map
Set
JSON
Math

流程:

1
V8

浏览器提供

例如:

1
2
3
4
5
6
7
8
9
document

fetch

setTimeout

EventSource

requestAnimationFrame

流程:

1
2
3
4
5
6
7
8
9
V8

↓

Binding

↓

Blink

6. Event Loop 的职责

Event Loop:

不是执行 JavaScript。

它负责:

决定什么时候让 JavaScript 获得执行机会。

模型:

1
2
3
4
5
6
7
8
9
10
11
12
13
Task Queue

↓

Event Loop

↓

V8

↓

执行JS

7. Task 和 Microtask

Task

来源:

  • 用户事件
  • setTimeout
  • 网络事件
  • SSE事件

例如:

1
2
setTimeout(()=>{
},0)

流程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
V8执行setTimeout

↓

Blink Timer注册

↓

时间到

↓

Task Queue

↓

Event Loop

↓

V8执行回调

Microtask

来源:

V8。

例如:

1
Promise.then()

流程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
V8

↓

Microtask Queue

↓

当前Task结束

↓

Microtask Checkpoint

↓

执行

8. 为什么 Promise 比 setTimeout 快?

因为:

执行顺序:

1
2
3
4
5
6
7
8
9
当前Task

↓

Microtask清空

↓

下一轮Task

例如:

1
2
3
4
5
6
7
8
9
10
11
12
console.log(1)

setTimeout(()=>{
console.log(2)
})

Promise.resolve()
.then(()=>{
console.log(3)
})

console.log(4)

结果:

1
2
3
4
1
4
3
2

原因:

Promise:

进入 Microtask。

setTimeout:

进入 Task。


9. Scheduler 的作用

Scheduler:

可以理解为:

浏览器的任务调度经理。

它负责:

  • 哪些任务优先
  • 什么时候处理输入
  • 什么时候给渲染机会

但是:

它不能抢占 JavaScript。


例如:

1
while(true){}

执行:

1
2
3
4
5
V8

↓

占用主线程

此时:

等待:

1
2
3
4
5
6
7
Timer Task

SSE Task

Render

Promise

都不能执行。


原因:

JavaScript:

Run To Completion

一个任务必须执行结束。


10. Rendering Pipeline

浏览器显示页面:

不是修改 DOM 就立即显示。

流程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
JavaScript修改DOM

↓

Style

↓

Layout

↓

Paint

↓

Composite

↓

GPU显示

11. requestAnimationFrame

requestAnimationFrame:

不是 Timer。

它属于:

浏览器渲染调度。

代码:

1
2
requestAnimationFrame(()=>{
})

流程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
V8执行调用

↓

Binding

↓

Blink记录callback

↓

等待下一次Rendering Opportunity

↓

执行callback

↓

Layout

↓

Paint

rAF 和 16.6ms

60Hz屏幕:

一帧:

约:

1
16.6ms

但是:

rAF不是:

剩多少时间执行。

而是:

下一次浏览器准备渲染时执行。


如果:

JS执行太久:

例如:

1
while(true){}

那么:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
16ms到了

↓

想渲染

↓

JS还没结束

↓

错过这一帧

↓

等待下一次机会

12. Microtask 为什么可能饿死渲染?

例如:

1
2
3
4
5
6
function loop(){
Promise.resolve()
.then(loop)
}

loop()

流程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Microtask

↓

产生Microtask

↓

继续执行

↓

产生Microtask

↓

无限循环

结果:

1
2
3
4
5
Render

↓

永远没有机会

原因:

浏览器规定:

Microtask Queue 必须清空。


13. 性能优化核心思想

浏览器不是帮你抢占 JS。

开发者需要主动让出执行权。

错误:

1
2
3
for(let i=0;i<10亿;i++){

}

优化:

拆分:

1
setTimeout(work,0)

或者:

1
requestAnimationFrame(work)

或者:

使用框架 Scheduler。


14. 最终完整模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
Browser Process


|
|
↓


Renderer Process


|
↓


Runtime


|
+----------------+
| |
↓ ↓

Scheduler Blink

| |
| |
↓ ↓

Event Loop Web API


|
↓


V8


|
↓


JavaScript


|
↓


Microtask


|
↓


Rendering Opportunity


|
↓


requestAnimationFrame


|
↓


Style

↓

Layout

↓

Paint

↓

Composite