{"componentChunkName":"component---src-templates-blog-post-js","path":"/classic-topic-what-happened-from-URL-input-to-page-rendering/index2/","result":{"data":{"markdownRemark":{"html":"<p>chrome浏览器是多进程架构，最上层的是浏览器进程（标签页外面的一切都由浏览器进程处理），然后是实用程序进程、渲染进程、GPU进程、插件进程。</p>\n<p>所以，当我们在地址栏输入aminer.cn</p>\n<h4>导航</h4>\n<ul>\n<li>\n<p>浏览器进程下面的UI线程去绘制浏览器的按钮和地址栏</p>\n</li>\n<li>\n<p>UI线程会判断用户输入的是查询字符串还是URL</p>\n</li>\n<li>\n<p>如果输入的是URL，UI线程会通知网络线程发起网络调用，获取网站内容（此时会先检查强缓存，如果命中直接返回网站内容）</p>\n</li>\n<li>\n<p>网络线程进行DNS查询（如果之前解析过这个域名，则直接命中缓存）、建立TLS连接（对于HTTPS）（如果返回301此时网络线程会跟UI线程沟通，再对另一个URL发送请求）</p>\n</li>\n<li>\n<p>通过三次握手建立 TCP 连接(即总共发送3个数据包确认已经建立连接)建立客户端和服务器之间的连接。然后进行数据包发送，接收方接收到数据包后必须要向发送方确认，否则会吃重复发送数据包，最后通过四次挥手断开链接</p>\n</li>\n<li>\n<p>构建请求行、请求头和请求体，发送 HTTP 请求</p>\n</li>\n<li>\n<p>收到响应数据，网络线程会检查接收到的前几个字节。响应的Content-Type头部应该包含数据类型，如果没有这个字段，则需要MIME类型嗅探</p>\n</li>\n<li>\n<p>如果是一个zip文件或其他文件，那就意味着是一个下载请求，需要把数据传给下载管理器。</p>\n</li>\n<li>\n<p>如果响应是HTML文件，要进行“安全浏览”检查，所有查检完毕，网络线程确认浏览器可以导航到用户请求的网站，于是会通知UI线程数据已经准备好了。UI线程会联系渲染器进程渲染网页。</p>\n</li>\n<li>\n<p>提交导航。数据和渲染器进程都有了，就可以通过IPC从浏览器进程向渲染器进程提交导航。渲染器进程也会同时接收到不间断的HTML数据流。当浏览器进程收到渲染器进程的确认消息后，导航完成，文档加载阶段开始。</p>\n</li>\n<li>\n<p>此时，地址栏会更新，安全指示图标和网站设置UI也会反映新页面的信息。当前标签页面的会话历史会更新，后退/前进按钮起作用。为便于标签页/会话在关闭标签页或窗口后恢复，会话历史会写入磁盘。</p>\n</li>\n<li>\n<p>提交导航之后，渲染器进程将负责加载资源和渲染页面（具体细节后面介绍）。而在“完成”渲染后（在所有iframe中的onload事件触发且执行完成后），渲染器进程会通过IPC给浏览器进程发送一个消息。此时，UI线程停止标签页上的旋转图标。</p>\n</li>\n</ul>\n<h3>渲染</h3>\n<ul>\n<li>\n<p>构建DOM。渲染器进程收到导航的提交消息后，开始接收HTML，其主线程开始解析文本字符串（HTML），并将它转换为DOM（Document Object Model，文档对象模型）。</p>\n</li>\n<li>\n<p>加载子资源。网站都会用到图片、CSS和JavaScript等外部资源。浏览器需要从缓存或网络加载这些文件。主线程可以在解析并构建DOM的过程中发现一个加载一个，但这样效率太低。为此，Chrome会在解析同时并发运行“预加载扫描器”，当发现HTML文档中有<img/>或<link/>时，预加载扫描器会将请求提交给浏览器进程中的网络线程。</p>\n</li>\n<li>\n<p>JavaScript可能阻塞解析。如果HTML解析器碰到<script/>标签，会暂停解析HTML文档并加载、解析和执行JavaScript代码。<a href=\"&#x27;&#x27;\">defer与async</a>因为JavaScript有可能通过document.write()修改文档，进而改变DOM结构</p>\n</li>\n<li>\n<p>主线程要解析CSS并计算每个DOM节点的样式</p>\n</li>\n<li>\n<p>主线程会遍历DOM元素及其计算样式，然后构造一棵布局树，这棵树的每个节点将带有坐标和大小信息。布局树与DOM树的结构类似，但只包含页面中可见元素的信息。如果元素被应用了display: none，则布局树中不会包含它（visibility: hidden的元素会包含在内）。类似地，通过伪类p::before{content: 'Hi!'}添加的内容会包含在布局树中，但DOM树中却没有。</p>\n</li>\n<li>\n<p>主线程会遍历布局树并创建绘制记录。绘制记录（绘制顺序）是对绘制过程的注解，比如“先画背景，然后画文本，最后画矩形”</p>\n</li>\n<li>\n<p>主线程会遍历布局树并创建分层树，然后主线程就会把这些信息提交给合成器线程。合成器线程接下来负责将每一层转换为像素——栅格化。一层有可能跟页面一样大，此时合成器线程会将它切成小片（tile），再把每一片发给栅格化线程。栅格化线程将每一小片转换为像素后将它们保存在GPU的内存中。will-change则是用来提醒浏览器该分层了</p>\n</li>\n<li>\n<p>合成器线程会安排栅格化线程优先转换视口（及附近）的小片。而构成一层的小片也会转换为不同分辨率的版本，以便在用户缩放时使用。</p>\n</li>\n<li>\n<p>创建好的合成器帧会通过IPC提交给浏览器进程。与此同时，为更新浏览器界面，UI线程可能还会添加另一个合成器帧；或者因为有扩展，其他渲染器进程也可能添加额外的合成器帧。所有这些合成器帧都会发送给GPU，以便最终显示在屏幕上。如果发生滚动事件，合成器线程会再创建新的合成器帧并发送给GPU。</p>\n</li>\n</ul>\n<h3>交互</h3>\n<ul>\n<li>\n<p>当用户交互比如触摸事件发生时，浏览器进程首先接收到该手势。但是，浏览器进程仅仅知道手势发生在哪里，因为标签页中的内容是渲染器进程处理。因此浏览器进程会把事件类型（如touchstart）及其坐标发送给渲染器进程。渲染器进程会处理这个事件，即根据事件目标来运行注册的监听程序。</p>\n</li>\n<li>\n<p>非快速滚动区”（non-fast scrollable region）。我们知道，运行JavaScript是主线程的活儿。在页面合成后，合成器线程会给附加了事件处理程序的页面区域打上“Non-Fast Scrollable Region”的记号。有了这个记号，合成器线程就可以在该区域发生事件时把事件发送给主线程。</p>\n</li>\n</ul>","timeToRead":2,"frontmatter":{"title":"经典题目：从输入URL到页面呈现发生了什么","date":"August 18, 2021","spoiler":null},"fields":{"slug":"/classic-topic-what-happened-from-URL-input-to-page-rendering/index2/"}}},"pageContext":{"slug":"/classic-topic-what-happened-from-URL-input-to-page-rendering/index2/","previous":{"fields":{"slug":"/color-transparency-hexadecimal-comparison-table/"},"frontmatter":{"title":"颜色透明度16进制对照表"}},"next":{"fields":{"slug":"/learn-about-the-problem-of-this-point/"},"frontmatter":{"title":"一文搞懂this指向问题"}}}},"staticQueryHashes":["3649515864","63159454"]}