-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbrowser.html
More file actions
472 lines (466 loc) · 143 KB
/
browser.html
File metadata and controls
472 lines (466 loc) · 143 KB
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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
<!doctype html>
<html lang="zh-Hans" dir="ltr" class="docs-wrapper plugin-docs plugin-id-default docs-version-current docs-doc-page docs-doc-id-browser" data-has-hydrated="false">
<head>
<meta charset="UTF-8">
<meta name="generator" content="Docusaurus v3.8.1">
<title data-rh="true">浏览器底层原理 | 缪斯</title><meta data-rh="true" name="viewport" content="width=device-width,initial-scale=1"><meta data-rh="true" name="twitter:card" content="summary_large_image"><meta data-rh="true" property="og:url" content="https://wooyee.cc/browser"><meta data-rh="true" property="og:locale" content="zh_Hans"><meta data-rh="true" name="docusaurus_locale" content="zh-Hans"><meta data-rh="true" name="docsearch:language" content="zh-Hans"><meta data-rh="true" name="docusaurus_version" content="current"><meta data-rh="true" name="docusaurus_tag" content="docs-default-current"><meta data-rh="true" name="docsearch:version" content="current"><meta data-rh="true" name="docsearch:docusaurus_tag" content="docs-default-current"><meta data-rh="true" property="og:title" content="浏览器底层原理 | 缪斯"><meta data-rh="true" name="description" content="浏览器架构"><meta data-rh="true" property="og:description" content="浏览器架构"><link data-rh="true" rel="icon" href="/img/favicon.ico"><link data-rh="true" rel="canonical" href="https://wooyee.cc/browser"><link data-rh="true" rel="alternate" href="https://wooyee.cc/browser" hreflang="zh-Hans"><link data-rh="true" rel="alternate" href="https://wooyee.cc/browser" hreflang="x-default"><script data-rh="true" type="application/ld+json">{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"浏览器底层原理","item":"https://wooyee.cc/browser"}]}</script><link rel="alternate" type="application/rss+xml" href="/about/rss.xml" title="缪斯 RSS Feed">
<link rel="alternate" type="application/atom+xml" href="/about/atom.xml" title="缪斯 Atom Feed">
<meta name="referrer" content="no-referrer"><link rel="stylesheet" href="/assets/css/styles.edfb6952.css">
<script src="/assets/js/runtime~main.8a790f23.js" defer="defer"></script>
<script src="/assets/js/main.d79500d5.js" defer="defer"></script>
</head>
<body class="navigation-with-keyboard">
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;"><defs>
<symbol id="theme-svg-external-link" viewBox="0 0 24 24"><path fill="currentColor" d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"/></symbol>
</defs></svg>
<script>!function(){var t="light";var e=function(){try{return new URLSearchParams(window.location.search).get("docusaurus-theme")}catch(t){}}()||function(){try{return window.localStorage.getItem("theme")}catch(t){}}();document.documentElement.setAttribute("data-theme",e||t),document.documentElement.setAttribute("data-theme-choice",e||t)}(),function(){try{const n=new URLSearchParams(window.location.search).entries();for(var[t,e]of n)if(t.startsWith("docusaurus-data-")){var a=t.replace("docusaurus-data-","data-");document.documentElement.setAttribute(a,e)}}catch(t){}}(),document.documentElement.setAttribute("data-announcement-bar-initially-dismissed",function(){try{return"true"===localStorage.getItem("docusaurus.announcement.dismiss")}catch(t){}return!1}())</script><div id="__docusaurus"><div role="region" aria-label="跳到主要内容"><a class="skipToContent_fXgn" href="#__docusaurus_skipToContent_fallback">跳到主要内容</a></div><div class="theme-announcement-bar announcementBar_mb4j" style="background-color:#fafbfc;color:#091E42" role="banner"><div class="announcementBarPlaceholder_vyr4"></div><div class="content_knG7 announcementBarContent_xLdY">⭐️ 如果这个网站能帮助到你,欢迎给一个 star 支持我!<a target="_blank" rel="noopener noreferrer" href="https://github.com/miusing/blog">GitHub</a></div><button type="button" aria-label="关闭" class="clean-btn close closeButton_CVFx announcementBarClose_gvF7"><svg viewBox="0 0 15 15" width="14" height="14"><g stroke="currentColor" stroke-width="3.1"><path d="M.75.75l13.5 13.5M14.25.75L.75 14.25"></path></g></svg></button></div><nav aria-label="主导航" class="theme-layout-navbar navbar navbar--fixed-top navbarHideable_m1mJ"><div class="navbar__inner"><div class="theme-layout-navbar-left navbar__items"><button aria-label="切换导航栏" aria-expanded="false" class="navbar__toggle clean-btn" type="button"><svg width="30" height="30" viewBox="0 0 30 30" aria-hidden="true"><path stroke="currentColor" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2" d="M4 7h22M4 15h22M4 23h22"></path></svg></button><a class="navbar__brand" href="/"><b class="navbar__title text--truncate">亲爱的缪斯</b></a></div><div class="theme-layout-navbar-right navbar__items navbar__items--right"><div class="navbarSearchContainer_Bca1"><div class="navbar__search searchBarContainer_NW3z" dir="ltr"><input placeholder="搜索" aria-label="Search" class="navbar__search-input searchInput_YFbd" value=""><div class="loadingRing_RJI3 searchBarLoadingRing_YnHq"><div></div><div></div><div></div><div></div></div></div></div><a aria-current="page" class="navbar__item navbar__link navbar__link--active" href="/intro">技术文档</a><a class="navbar__item navbar__link" href="/about">关于我</a><a href="https://github.com/miusing/blog" target="_blank" rel="noopener noreferrer" class="navbar__item navbar__link">GitHub<svg width="13.5" height="13.5" aria-hidden="true" class="iconExternalLink_nPIU"><use href="#theme-svg-external-link"></use></svg></a><div class="toggle_vylO colorModeToggle_DEke"><button class="clean-btn toggleButton_gllP toggleButtonDisabled_aARS" type="button" disabled="" title="system mode" aria-label="切换浅色/暗黑模式(当前为system mode)"><svg viewBox="0 0 24 24" width="24" height="24" aria-hidden="true" class="toggleIcon_g3eP lightToggleIcon_pyhR"><path fill="currentColor" d="M12,9c1.65,0,3,1.35,3,3s-1.35,3-3,3s-3-1.35-3-3S10.35,9,12,9 M12,7c-2.76,0-5,2.24-5,5s2.24,5,5,5s5-2.24,5-5 S14.76,7,12,7L12,7z M2,13l2,0c0.55,0,1-0.45,1-1s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S1.45,13,2,13z M20,13l2,0c0.55,0,1-0.45,1-1 s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S19.45,13,20,13z M11,2v2c0,0.55,0.45,1,1,1s1-0.45,1-1V2c0-0.55-0.45-1-1-1S11,1.45,11,2z M11,20v2c0,0.55,0.45,1,1,1s1-0.45,1-1v-2c0-0.55-0.45-1-1-1C11.45,19,11,19.45,11,20z M5.99,4.58c-0.39-0.39-1.03-0.39-1.41,0 c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0s0.39-1.03,0-1.41L5.99,4.58z M18.36,16.95 c-0.39-0.39-1.03-0.39-1.41,0c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0c0.39-0.39,0.39-1.03,0-1.41 L18.36,16.95z M19.42,5.99c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06c-0.39,0.39-0.39,1.03,0,1.41 s1.03,0.39,1.41,0L19.42,5.99z M7.05,18.36c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06 c-0.39,0.39-0.39,1.03,0,1.41s1.03,0.39,1.41,0L7.05,18.36z"></path></svg><svg viewBox="0 0 24 24" width="24" height="24" aria-hidden="true" class="toggleIcon_g3eP darkToggleIcon_wfgR"><path fill="currentColor" d="M9.37,5.51C9.19,6.15,9.1,6.82,9.1,7.5c0,4.08,3.32,7.4,7.4,7.4c0.68,0,1.35-0.09,1.99-0.27C17.45,17.19,14.93,19,12,19 c-3.86,0-7-3.14-7-7C5,9.07,6.81,6.55,9.37,5.51z M12,3c-4.97,0-9,4.03-9,9s4.03,9,9,9s9-4.03,9-9c0-0.46-0.04-0.92-0.1-1.36 c-0.98,1.37-2.58,2.26-4.4,2.26c-2.98,0-5.4-2.42-5.4-5.4c0-1.81,0.89-3.42,2.26-4.4C12.92,3.04,12.46,3,12,3L12,3z"></path></svg><svg viewBox="0 0 24 24" width="24" height="24" aria-hidden="true" class="toggleIcon_g3eP systemToggleIcon_QzmC"><path fill="currentColor" d="m12 21c4.971 0 9-4.029 9-9s-4.029-9-9-9-9 4.029-9 9 4.029 9 9 9zm4.95-13.95c1.313 1.313 2.05 3.093 2.05 4.95s-0.738 3.637-2.05 4.95c-1.313 1.313-3.093 2.05-4.95 2.05v-14c1.857 0 3.637 0.737 4.95 2.05z"></path></svg></button></div></div></div><div role="presentation" class="navbar-sidebar__backdrop"></div></nav><div id="__docusaurus_skipToContent_fallback" class="theme-layout-main main-wrapper mainWrapper_z2l0"><div class="docsWrapper_hBAB"><button aria-label="回到顶部" class="clean-btn theme-back-to-top-button backToTopButton_sjWU" type="button"></button><div class="docRoot_UBD9"><aside class="theme-doc-sidebar-container docSidebarContainer_YfHR"><div class="sidebarViewport_aRkj"><div class="sidebar_njMd sidebarWithHideableNavbar_wUlq"><a tabindex="-1" class="sidebarLogo_isFc" href="/"><b>亲爱的缪斯</b></a><nav aria-label="文档侧边栏" class="menu thin-scrollbar menu_SIkG menuWithAnnouncementBar_GW3s"><ul class="theme-doc-sidebar-menu menu__list"><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-1 menu__list-item"><a class="menu__link" href="/intro">欢迎来到缪斯的技术博客</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-1 menu__list-item"><a class="menu__link menu__link--active" aria-current="page" href="/browser">浏览器底层原理</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-1 menu__list-item"><a class="menu__link" href="/HTML">HTML</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-1 menu__list-item"><a class="menu__link" href="/css">CSS</a></li><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item menu__list-item--collapsed"><div class="menu__list-item-collapsible"><a class="menu__link menu__link--sublist" href="/javascript/basic">JavaScript</a><button aria-label="展开侧边栏分类 'JavaScript'" aria-expanded="false" type="button" class="clean-btn menu__caret"></button></div></li><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item menu__list-item--collapsed"><div class="menu__list-item-collapsible"><a class="menu__link menu__link--sublist menu__link--sublist-caret" role="button" aria-expanded="false" href="/typescript/config">Typescript</a></div></li><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item menu__list-item--collapsed"><div class="menu__list-item-collapsible"><a class="menu__link menu__link--sublist menu__link--sublist-caret" role="button" aria-expanded="false" href="/node/module">Node</a></div></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-1 menu__list-item"><a class="menu__link" href="/vue">Vue</a></li><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item menu__list-item--collapsed"><div class="menu__list-item-collapsible"><a class="menu__link menu__link--sublist" href="/react">React</a><button aria-label="展开侧边栏分类 'React'" aria-expanded="false" type="button" class="clean-btn menu__caret"></button></div></li><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item menu__list-item--collapsed"><div class="menu__list-item-collapsible"><a class="menu__link menu__link--sublist" href="/infra/前端工程化">前端工程化</a><button aria-label="展开侧边栏分类 '前端工程化'" aria-expanded="false" type="button" class="clean-btn menu__caret"></button></div></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-1 menu__list-item"><a class="menu__link" href="/binary">二进制与编码</a></li><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item menu__list-item--collapsed"><div class="menu__list-item-collapsible"><a class="menu__link menu__link--sublist menu__link--sublist-caret" role="button" aria-expanded="false" href="/canvas">Canvas</a></div></li><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item menu__list-item--collapsed"><div class="menu__list-item-collapsible"><a class="menu__link menu__link--sublist menu__link--sublist-caret" role="button" aria-expanded="false" href="/canvas/webgl">WebGL</a></div></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-1 menu__list-item"><a class="menu__link" href="/git">Git</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-1 menu__list-item"><a class="menu__link" href="/音视频学习">音视频学习</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-1 menu__list-item"><a class="menu__link" href="/计算机网络">计算机网络</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-1 menu__list-item"><a class="menu__link" href="/前端安全">前端安全</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-1 menu__list-item"><a class="menu__link" href="/异常处理">异常处理</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-1 menu__list-item"><a class="menu__link" href="/编译原理">编译原理</a></li><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item menu__list-item--collapsed"><div class="menu__list-item-collapsible"><a class="menu__link menu__link--sublist menu__link--sublist-caret" role="button" aria-expanded="false" href="/rust/setup">Rust</a></div></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-1 menu__list-item"><a class="menu__link" href="/网站优化">性能优化</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-1 menu__list-item"><a class="menu__link" href="/数据结构">数据结构</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-1 menu__list-item"><a class="menu__link" href="/排序">排序算法</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-1 menu__list-item"><a class="menu__link" href="/编程题">编程题</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-1 menu__list-item"><a class="menu__link" href="/mongodb">MongoDB</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-1 menu__list-item"><a class="menu__link" href="/mysql">Mysql</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-1 menu__list-item"><a class="menu__link" href="/linux">Linux</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-1 menu__list-item"><a class="menu__link" href="/docker">Docker</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-1 menu__list-item"><a class="menu__link" href="/设计模式">设计模式</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-1 menu__list-item"><a class="menu__link" href="/websocket">WebSocket</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-1 menu__list-item"><a class="menu__link" href="/webgl">Three.js</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-1 menu__list-item"><a class="menu__link" href="/php">php</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-1 menu__list-item"><a class="menu__link" href="/写在前面">写在前面</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-1 menu__list-item"><a class="menu__link" href="/其它">其它</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-1 menu__list-item"><a class="menu__link" href="/代码段记录">代码段记录</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-1 menu__list-item"><a class="menu__link" href="/第三方库">第三方库</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-1 menu__list-item"><a class="menu__link" href="/面试题">面试题</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-1 menu__list-item"><a class="menu__link" href="/akara的考验">akara的考验</a></li></ul></nav><button type="button" title="收起侧边栏" aria-label="收起侧边栏" class="button button--secondary button--outline collapseSidebarButton_PEFL"><svg width="20" height="20" aria-hidden="true" class="collapseSidebarButtonIcon_kv0_"><g fill="#7a7a7a"><path d="M9.992 10.023c0 .2-.062.399-.172.547l-4.996 7.492a.982.982 0 01-.828.454H1c-.55 0-1-.453-1-1 0-.2.059-.403.168-.551l4.629-6.942L.168 3.078A.939.939 0 010 2.528c0-.548.45-.997 1-.997h2.996c.352 0 .649.18.828.45L9.82 9.472c.11.148.172.347.172.55zm0 0"></path><path d="M19.98 10.023c0 .2-.058.399-.168.547l-4.996 7.492a.987.987 0 01-.828.454h-3c-.547 0-.996-.453-.996-1 0-.2.059-.403.168-.551l4.625-6.942-4.625-6.945a.939.939 0 01-.168-.55 1 1 0 01.996-.997h3c.348 0 .649.18.828.45l4.996 7.492c.11.148.168.347.168.55zm0 0"></path></g></svg></button></div></div></aside><main class="docMainContainer_TBSr"><div class="container padding-top--md padding-bottom--lg"><div class="row"><div class="col docItemCol_VOVn"><div class="docItemContainer_Djhp"><article><nav class="theme-doc-breadcrumbs breadcrumbsContainer_Z_bl" aria-label="页面路径"><ul class="breadcrumbs"><li class="breadcrumbs__item"><a aria-label="主页面" class="breadcrumbs__link" href="/"><svg viewBox="0 0 24 24" class="breadcrumbHomeIcon_YNFT"><path d="M10 19v-5h4v5c0 .55.45 1 1 1h3c.55 0 1-.45 1-1v-7h1.7c.46 0 .68-.57.33-.87L12.67 3.6c-.38-.34-.96-.34-1.34 0l-8.36 7.53c-.34.3-.13.87.33.87H5v7c0 .55.45 1 1 1h3c.55 0 1-.45 1-1z" fill="currentColor"></path></svg></a></li><li class="breadcrumbs__item breadcrumbs__item--active"><span class="breadcrumbs__link">浏览器底层原理</span></li></ul></nav><div class="tocCollapsible_ETCw theme-doc-toc-mobile tocMobile_ITEo"><button type="button" class="clean-btn tocCollapsibleButton_TO0P">本页总览</button></div><div class="theme-doc-markdown markdown"><header><h1>浏览器底层原理</h1></header>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="浏览器架构">浏览器架构<a href="#浏览器架构" class="hash-link" aria-label="浏览器架构的直接链接" title="浏览器架构的直接链接"></a></h2>
<p>现代浏览器都采用多进程的架构设计,主要包括以下进程:</p>
<ul>
<li>浏览器进程(主进程)</li>
<li>渲染进程(多个)<!-- -->
<ul>
<li>主线程(main thread):Blink渲染引擎,负责页面的渲染(HTML解析、CSS解析、Layout、Paint);Blink内置了V8引擎,负责JavaScript的解释执行。</li>
<li>合成器线程(impl thread ):在早期的Chrome设计中并不存在,后续引入合成器线程主要目的是将布局树拆分为多个图层Layer进行独立的光栅化和渲染,最终再重新合成为一张位图。</li>
</ul>
</li>
<li>GPU进程<!-- -->
<ul>
<li>内部通过2D图形库Skia间接调用OpenGL接口来实现画面的绘制。</li>
</ul>
</li>
<li>网络进程<!-- -->
<ul>
<li>负责网络通信等功能。</li>
</ul>
</li>
<li>插件进程(多个)</li>
</ul>
<blockquote>
<p>HTML和Canvas的渲染,本质都是在主线程产出绘制指令交给GPU进程的Skia来间接OpenGL绘制,这也是为什么他们都能享受到GPU提供的硬件加速能力(如借助GPU执行Shader来快速地实现类似高斯模糊的效果,如果在CPU主线程实现则是巨大的开销 )</p>
<p>而WebGL则是允许我们直接和GPU进行交互,少去了中间层层封装引入的开销,也允许我们更好的定制化功能。</p>
</blockquote>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="浏览器渲染流程">浏览器渲染流程<a href="#浏览器渲染流程" class="hash-link" aria-label="浏览器渲染流程的直接链接" title="浏览器渲染流程的直接链接"></a></h2>
<ul>
<li>构建DOM树(Parse HTML)<!-- -->
<ul>
<li>通过HTML解析器实现字符流 -> token流 -> 抽象语法树(即DOM树)</li>
</ul>
</li>
<li>样式计算(Recalculate style)<!-- -->
<ul>
<li>通过CSS解析器生成styleSheets,也被称为CSSOM,可以通过<code>window.styleSheets</code>访问。</li>
<li>属性值标准化,把类似<code>color: red</code>等非标准值转化为<code>color: rgb(255, 0, 0)</code>。</li>
<li>计算出每个DOM节点的最终样式并存在内部的一个被称作ComputedStyle的巨大Map中,可以通过<code>window.getComputedStyle</code>访问特定DOM节点的值。</li>
</ul>
</li>
<li>布局(Layout)<!-- -->
<ul>
<li>我们已经构建了DOM树和所有节点的样式信息,那么就可以生成一个布局树(有的地方也称作渲染树),这一步还会去掉原本DOM树上不可见的节点(比如<code><head /></code>标签或应用了如<code>{ display: none }</code>样式的节点)</li>
<li>使用复杂的算法计算出每个节点的绝对<strong>坐标信息</strong>(x、y、width、height)</li>
</ul>
</li>
<li>分层(Layerise)<!-- -->
<ul>
<li>分层是一个<strong>性能优化</strong>手段,在早期的Chrome架构实现其实是不存在分层这一步骤的。</li>
<li>简单来说的话,分层可以把一颗完整的布局树拆分成多棵子树(Layers),后续再独立光栅化来绘制,最终重新合成为一张完整的位图。这么做的一个好处是避免局部状态更新触发全局的渲染。</li>
<li>怎样的节点会被提升到一个独立的渲 染层当中:(TODO)</li>
</ul>
</li>
<li>预先绘制(Pre Paint)<!-- -->
<ul>
<li>涉及到属性树的生成,暂且不提。</li>
</ul>
</li>
<li>绘制(Paint)<!-- -->
<ul>
<li>这一步的绘制并不是真的把内容绘制到屏幕上。这里说的绘制的本质是对布局树的解释执行,对于每一个待绘制的节点都能得到一组<strong>绘制指令</strong>,这个绘制指令会在后续的流程中用来实现真正的画面绘制。</li>
</ul>
</li>
<li>提交(Commit)<!-- -->
<ul>
<li>主线程Paint生成的绘制指定提交给合成器线程。这一步之前的操作都发生在主线程,这一步之后的操作主要发生在合成器线程,光栅化则是发生在GPU进程当中。</li>
</ul>
</li>
<li>分块(tiling)<!-- -->
<ul>
<li>这也是一个<strong>性能优化</strong>手段。光栅化整个Layer是比较昂贵的开销,特别是我们不需要光栅化可视区域外的内容,因此我们可以把Layer切分成多个图块进行光栅化。</li>
</ul>
</li>
<li>光栅化(raster)<!-- -->
<ul>
<li>光栅化即通过绘制指令生成位图的过程,也就是真正的绘制操作。在Chrome内部这是通过运行在GPU进程中的图形库Skia实现的,而Skia的底层其实是基于OpenGL的。通常光栅器的实现有基于CPU实现的软光栅器,比如使用线性扫描算法实现逐像素的填充;除此之外还有基于GPU实现的光栅器(即硬件加速),通过片段着色器VS实现并行的计算逻辑。</li>
<li>光栅化还包括图像的解码过程,如<code><img src="./cat.png" /></code></li>
</ul>
</li>
<li>合成<!-- -->
<ul>
<li>分块(tile)经过光栅化的产物得到了位图quads,然后通过合成器线程执行<code>draw quads</code>则会把这些位图合并成一张完整的位图写入帧缓冲当中,显卡读取后显示在显示器上被我们看到。</li>
</ul>
</li>
</ul>
<blockquote>
<p>补充:</p>
<p>上述流程是早先的Chrome渲染架构,和最新 版本的实现会有部分细节出入,但整个思想是一致的。最新版本Chromium已经把分层Layerise这一步骤放在绘制Paint这一步之后了,以及光栅化可能也会用一个新的worker线程来跑这些细节差异。</p>
<p>参考:</p>
<ol>
<li><a href="https://docs.google.com/presentation/d/1boPxbgNrTU0ddsc144rcXayGA_WF53k96imRH8Mp34Y/edit#slide=id.ga884fe665f_64_6" target="_blank" rel="noopener noreferrer">https://docs.google.com/presentation/d/1boPxbgNrTU0ddsc144rcXayGA_WF53k96imRH8Mp34Y/edit#slide=id.ga884fe665f_64_6</a></li>
</ol>
</blockquote>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="重排reflow">重排(Reflow)<a href="#重排reflow" class="hash-link" aria-label="重排(Reflow)的直接链接" title="重排(Reflow)的直接链接"></a></h3>
<p>当我们页面渲染完成后,即使我们什么都不操作显示器也会根据特定的频率读取帧缓冲的内容进行显示。为了让用户看到的内容发生变化,我们需要反复触发HTML渲染管线写入新的数据到帧缓冲当中。当然了,我们不需要重新走这个完整流程,而是可以复用之前创建的布局树这个数据源,比如只修改某个渲染层的某个节点的背景颜色,这样就不需要进行重新布局这样的复杂计算了。</p>
<p>但有的时候,比如我们需要新增DOM节点,这样会导致我们的布局信息发生变化,因此我们需要重新执行布局这一步骤。这也被称之为重排Reflow。布局涉及到复杂的坐标计算,是个比较花费性能的操作,频繁触发重排会影响整个页面的渲染体验。</p>
<p>哪些常见操作会触发重排:</p>
<ol>
<li>添加或删除DOM元素</li>
<li>修改DOM元素的几何属性(x、y、width、height)</li>
</ol>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="重绘repaint">重绘(Repaint)<a href="#重绘repaint" class="hash-link" aria-label="重绘(Repaint)的直接链接" title="重绘(Repaint)的直接链接"></a></h3>
<p>如果我们只是修改了某个DOM节点的背景颜色,那么我们的布局树只是某个属性发生了变化,不需要重新进行复杂的坐标计算。只是在绘制的步骤中需要解析成一个不同的绘制指令,整体的性能开销是比较小的。</p>
<p>哪些常见操作会触发重绘:</p>
<ol>
<li>修改DOM节点的颜色、背景颜色等非几何信息</li>
</ol>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="合成composite">合成(Composite)<a href="#合成composite" class="hash-link" aria-label="合成(Composite)的直接链接" title="合成(Composite)的直接链接"></a></h3>
<p>我们都知道,主线程中可用来渲染一帧的时间是宝贵的16ms。其中一部分时间会被用来执行JS,剩下的时间才是主线程的渲染阶段,渲染所需要花费的时间越长则越可能引起页面的卡顿。那么,我们有没有办法在修改DOM属性时不触发重排或者重绘,来优化页面性能呢?</p>
<p>答案是有的。我们可以把一些操作放到合成器线程中执行,比如CSS3的<code>transform</code>与其在主线程中计算出新的绘制指令,我们可以在合成器线程中实现样式调整,从而缓解主线程的开销。除了<code>transform</code>还有<code>opacity</code>、<code>filter</code>等属性也能享受到一样的性能提升,网上也称之为<strong>CSS硬件加速</strong>,我猜测可能是合成器线程又和渲染进程通信来借助了GPU的并行计算能力来实现的。</p>
<h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="强制同步布局forced-synchronous-layouts">强制同步布局(forced synchronous layouts)<a href="#强制同步布局forced-synchronous-layouts" class="hash-link" aria-label="强制同步布局(forced synchronous layouts)的直接链接" title="强制同步布局(forced synchronous layouts)的直接链接"></a></h3>
<p><img decoding="async" loading="lazy" src="https://web.dev/static/articles/avoid-large-complex-layouts-and-layout-thrashing/image/using-flexbox-layout-0c9955c54a296_1920.jpg" alt="" class="img_ev3q"></p>
<p>渲染一帧的顺序可以简化为上图。当我们通过JavaScript尝试访问某个DOM节点的几何信息时,实际上是通过上次渲染时的布局树拿到的几何信息。当我们先修改了DOM节点的样式后,浏览器会认为节点的几何信息也可能发生变更,因此当我们再去尝试读取节点的几何坐标信息时,浏览器会强制性<strong>重新计算样式并重新布局</strong>来获取到最新的几何信息。这样会带来高昂的性能成本。</p>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// 先写后读。触发了强制同步布局,性能劣化</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">logBoxHeight</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> box</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">classList</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">add</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'super-big'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token console class-name">console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">log</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">box</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">offsetHeight</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>一般来说我们先读后写就好,其实读取到的是上一帧渲染时布局树的几何信息也问题不大。下面的代码也是类似的,整个JS任务执行的过程中出现了多次先写后读,疯狂的重排导致性能劣化更加严重。</p>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">resizeAllParagraphsToMatchBlockWidth</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 第一个循环中写入样式;第二个循环中读取样式</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">for</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">let</span><span class="token plain"> i </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> i </span><span class="token operator" style="color:#393A34"><</span><span class="token plain"> paragraphs</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">length</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> i</span><span class="token operator" style="color:#393A34">++</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> paragraphs</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">i</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">style</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">width</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation">box</span><span class="token template-string interpolation punctuation" style="color:#393A34">.</span><span class="token template-string interpolation property-access">offsetWidth</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string string" style="color:#e3116c">px</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>当触发强制同步布局时,我们可以在Chrome开发者工具性能面板中对应的样式计算中看到Recalculation forced的标识,以及对应的</p>
<blockquote>
<p>TODO:</p>
<ol>
<li>重排的范围</li>
<li>性能优化</li>
<li>渲染层提升</li>
</ol>
</blockquote>
<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="输入url后发生了什么">输入URL后发生了什么<a href="#输入url后发生了什么" class="hash-link" aria-label="输入URL后发生了什么的直接链接" title="输入URL后发生了什么的直接链接"></a></h2>
<p>经常遇到的问题,我们可以在浏览器渲染流程的基础上展开去聊,简单来说包括以下关键知识点(部分细节有省略):</p>
<ol>
<li>
<p>网络通信过程</p>
<ol>
<li>
<p>URL解析得到协议、域名、端口、路径</p>
</li>
<li>
<p>DNS域名解析</p>
<ol>
<li>
<p>递归查找缓存。先依次尝试从浏览器缓存、操作系统缓存、Hosts文件、路由器缓存、本地DNS服务器(即本地网络中设置的DNS服务器地址)缓存,解析到域名对应的IP地址。</p>
</li>
<li>
<p>迭代DNS服务器。</p>
<ol>
<li>向根DNS服务器查询顶级域DNS服务器的地址</li>
<li>向顶级域DNS服务器查询权威域名DNS服务器的地址</li>
<li>向权威域名DNS服务器查询目标域名对应的IP地址</li>
</ol>
<p><img decoding="async" loading="lazy" src="https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/1/30/16ff45e132f02931~tplv-t2oaga2asx-jj-mark:3024:0:0:0:q75.png" alt="" class="img_ev3q"></p>
</li>
</ol>
</li>
<li>
<p>三次握手建立TCP连接</p>
<ol>
<li>客户端发送一个SYN=1,Seq=X的TCP包</li>
<li>服务端返回一个SYN=1,ACK=X+1,Seq=Y的TCP包</li>
<li>客户端发送一个ACK=Y+1,Seq=Y + 1的TCP包</li>
</ol>
</li>
<li>
<p>(可选)如果是HTTPS(具体实现可见计算机网络章节)</p>
<ol>
<li>TLS握手,交换版本信息、加密算法、压缩算法、随机数(浏览器一个,客户端一个)。</li>
<li>服务端发送证书(包括公钥和CA私钥对该公钥的签名)给客户端,客户端使用CA公钥对签名进行验证。</li>
<li>使用服务器公钥生成对称密钥,用户后续的加密解密(实际的实现要复杂亿点点)</li>
</ol>
</li>
<li>
<p>发送HTTP请求并接收响应。</p>
<ol>
<li>查看浏览器内是否有资源的缓存<!-- -->
<ol>
<li>存在缓存,检查缓存是否过期<!-- -->
<ol>
<li>