用户从输入URL到页面展示,发生了什么?

147 阅读5分钟

导航流程

  1. 浏览器进程根据用户的输入来判断是搜索内容还是网址,如果是搜索内容,则使用默认的搜索引擎拼接搜索内容成新的URL来继续;如果是网址,则根据URL拼接合适的协议形成新的URL;
  2. 用户回车,此时该界面呈loading状态;
  3. 浏览器进程将URL发送给网络进程(通过进程间的通信IPC);
  4. 网络进程获取到URL后先去本地缓存去查找是否有对应的资源,如果有则拦截请求,直接返回200,否则进入网络请求过程;
  5. 网络进程请求DNS返回对应的IP和端口号(这里涉及到DNS查找的过程),首先是去本地查找是否缓存过域名,如果有就直接返回,如果没有就是逐级查找的过程:去顶级域(.com,.cn,.pro...)查找根域名的IP(例:baidu.com、taobao.com、juejin.cn...),查找到后在根域名服务器上查找三级域的地址(www.baidu.com, www.juejin.cn...) 解析出当前域名的IP和端口,如果没有端口,http默认80,https 默认443,如果是https,还需要建立TLS连接;
  6. 接着3次握手建立TCP连接,http请求加上TCP头(包括源端口号,目的端口号和保证数据顺序的序号)向下传输;
  7. 网络层加上IP头部(包括源IP地址,目的IP地址)接着向下传输;
  8. 底层通过物理网络传输给目的服务器主机
  9. 目的服务器主机的网络层接收到数据包,解析出IP头部,数据包,将数据包向上传输;
  10. 目的服务器主机的传输层收到数据包后解析出TCP头部,端口号和数据包,将数据包向上一层应用层传输;
  11. 目的服务器主机的应用层HTTP根据请求头和请求体来处理,如果该请求需要重定向,则返回301/302,以及对应的重定向URL返回。非重定向状态下,根据请求头中的缓存字段:Cache Control,If-Modified-Since, If-None-Match来等来决定内容是否过期,强缓存优先级协商缓存优先级,,若未过期则直接返回304, 若过期则返回新鲜数据。
  12. 响应数据又顺着应用层-传输层-网络层-物理链路层-源主机-源主机网络层-源主机传输层-源主机应用层返回到源主机浏览器的网络进程
  13. 数据传接收成,TCP4次挥手断开连接,若HTTP头部有connection:keep-alive 信息,则该TCP连接不断开;
  14. 网络进程接解析出来HTTP的响应头数据,将其转发给浏览器进程。浏览器进程根据相应内容类型:content-type来进行接下来的操作,如果是下载类型,则浏览器进程会提交给浏览器的下载管理器,至此该URL的导航流程也结束。如果是HTML,则浏览器进程会发送CommitNavigation消息到渲染进程,发送时会带着响应头及相应内容。
  15. 渲染进程接收到CommitNavigation 消息后便开始准备接收HTML数据,接收方式是直接和网络进程建立数据管道。
  16. 渲染进程向浏览器进程“确认提交”,这是告诉浏览器说我已经准备好接收和解析HTML 资源了;
  17. 浏览器进程更新页面状态(更新前进/后退箭头的状态/,loading态等);

渲染流程

  1. 将HTML转化成DOM树

  2. 样式计算: 生成styleSheets,将样式表中的属性值标准化,计算DOM树中每个节点的具体样式(涉及到CSS集成规则和层叠规则)

  3. 布局阶段(计算DOM树中可见元素的几何位置,这个过程叫做布局):

    • 创建布局树(Layout tree),其实就是DOM Tree + ComputedStyle = Layout Tree
      
    • 布局计算
      
  4. 分层,对布局树分层(对于特殊的CSS属性,渲染引擎会为特定的节点生成专用的图层Layer Tree):

    •  拥有层叠上下文属性的元素会被提升为单独的一层
      
    •  需要裁剪(clip)的地方会创建为图层
      
  5. 图层绘制

    •  渲染引擎会把图层拆分成很多很小的绘制指令,按照顺序组成待绘制列表;
      
    •  渲染进程中的主线程将绘制列表提交给合成线程(如下图所示)
      
    • merge-thread.jpg

    • 合成线程将图层划分为很多很小的图块,并在光栅化线程池中将图块转换为位图

    • thread1.jpg

    • 通常栅化过程通常都会使用到GPU来加速,生成的位图会被保存到GPU内存中(GPU运行在GPU进程中,这里涉及到了进程间的通信)

    • thread3.jpg

  6. 合成与显示

    • 当所有的图块都被光栅化后,合成线程会提交一个DrawQuad命令给浏览器进程
    • 浏览器进程接收到该命令后会将页面绘制到内存中,最终再将内存展示到显示器上
    • thread4.jpg

至此从用户输入URL回车到页面显示,整个过程如上文所述。参考《浏览器工作原理与实践》

简易流程图

graph LR
DOM --- Style --- LayoutTree --- LayerTree --- Paint --- Tiles --- Raster --- DrawQuad --- Display

从上述过程可以很好的理解为什么重排会比重绘更消耗性能,重排会从第一步一直直走到最后,而重绘不需要走 布局流程,即跳过Layout,Layer。如下图所示

重排

demo2.jpg 重绘

demo1.jpg

合成

demo3.jpg

可以通过css 属性(will-change:transform,transform 等)避开重排和重绘阶段,直接在非主线程上执行合成操作。这样的效率是最高的,因为是在非主线程上合成,并没有占用主线程的资源,另外也避开了布局和绘制两个子阶段,所以相对于重绘和重排,合成能大大提升绘制效率。