浏览器从输入URL到渲染

282 阅读6分钟

基于webkit和blink内核的浏览器讨论

请求阶段

  1. DNS解析与HTTP连接

首先Chrome搜索自身的DNS缓存,如果没有找到或者缓存失效的话,就读hosts文件,如果还是没有找到,就像运营商发起DNS解析请求,获取IP,然后三次握手建立TCP连接,连接建立完成之后就发送HTTP请求,获取页面信息,对于HTTP/1.1支持长连接,请求头默认带keep-alive,对于长连接的话,响应完成之后服务端是不会关闭HTTP连接的,所以一般会在响应头里添加响应完成的标志,一般是Content-Length

Webkit网络请求
这个过程中,浏览器会调用网络模块建立连接和请求,对于Webkit来说,有WebCroWebkit Ports两个部分,其中网络请求部分是基于操作系统的网络库来实现的,它属于Webkit Ports里的,它在不同的Webkit内核浏览器中的实现也不一样,WebCro主要是关于HTML,css解析,布局计算和渲染的,Webkit Ports主要是关于调用操作系统的模块进行网络请求,图片解码,视频文字渲染以及硬件加速的功能。

DNS预取和TCP预连接
用户在浏览网页的时候,Chrome会提取网页中的超链接,拿到域名,利用较少的CPU和带宽解析这些域名。

<link rel="dns-prefetch" href="..." />

还可能会进行TCP预连接

TCP三次握手

  1. 客户端将SYN置为1,随机生成一个seq,然后把数据包发送给服务端,客户端进入SYN_SEND状态
  2. 服务端查看标志位SYN,如果是1,就说明是有一个客户端请求TCP连接,然后服务端设置标志位ACK为1,设置ack=seq + 1,再随机生成一个seq,返回给客户端,服务端进入SYN_RECEIVED状态
  3. 客户端收到响应之后,判断如果ACK标志位是1的话,就说明是服务端收到了请求,然后就设置ack = seq + 1ACK标志位为1,发送给服务端
  4. 服务端检测ack是否正确,如果正确的话,就成功建立的TCP连接

渲染阶段

  1. 获取HTML页面,进行初步解析

一般情况下服务端响应的是一个HTML页面,Chrome拿到HTML页面之后,从头开始解析,首先,Brower进程接收到用户的请求之后通过UI线程处理,然后将任务转交给Render进程,Renderer进程开启一个单独的渲染线程解析HTML文档,WebCro读取HTML网页,解释器检查网页的编码格式,使用对应的解码器进行解析,一般编码格式写在meta中,国内主要有utf-8Unicodegbk这几类格式。
然后就可以进行词法分析了,把HTML标签解析成对应的token,解析的时候Webkit会过滤掉xss攻击的代码。
如果解析的时候碰到了js代码或者script标签,HTML会暂停解析,调用js引擎执行js代码,或者调用网络模块获取js脚本文件,对于Chrome来说是v8,对于其他webkit内核的浏览器可能是JavaScriptcro,因为js引擎和webkit不是绑定在一起的。因为js代码虽然现在无法访问dom,但可能会调用document.write修改dom结构。如果这个时候不解析HTML的话,后续的资源也无法获取,就很亏,所以Webkit有一个预扫描和预加载机制,预扫描之后的词语,然后提前加载需要的资源。
最终得到一个dom树,可以通过控制台简单显示这个dom树的结构。

优化HTML解析的策略

js 加载的时候尽量使用defer或者sync,或者将js脚本写在HTML的下面

Webkit进程模型

Brower进程:负责显示界面,管理各个页面进程的创建和销毁,所以页面崩溃不会导致浏览器崩溃 Renderer进程:负责网页的渲染不过不是一个标签页一个renderer进程,具体如何分配不是很清楚
GPU进程:在开启了GPU加速时会被创建,只有一个GPU进程

  1. 对css进行解析

在HTML的dom树解析完成之后,开始解析css,一般情况下浏览器会有一个默认的样式,建立完了样式规则之后,Webkit会为一些可视的节点通过标签名和类等匹配样式。css dom 可以确定节点对象的计算样式

  1. Render Object

RenderObject包括document节点,可见节点和一些Webkit需要的节点包括了绘制的各种信息,是经过css dom和HTML dom计算得到的,然后通过这个RenderObject使用盒模型计算dom节点的布局,大小等属性

  1. 生成RenderLayer

为了方便渲染Webkit会为一些特定的dom生成RenderLayer层次,所有的RenderObject子树都属于某一个RenderLayer层次 CSS position,video,canvas(调用GPU硬件加速),overflow,有透明度的Render Object都会生成一层RenderLayer

  1. 绘制

接着,Webkit开始绘制每一层的Renderlayer图形,部分2D图形或者文字通过CPU绘制,3D或者复杂的变换通过GPU绘制,然后合成这些图层,这有个好处就是如果某些css变化之后我们可以绘制这一层的图形然后再合并起来

之后页面也就绘制完成了,接下来可能会发生回流或者重绘

浏览器回流重绘

会导致回流的操作:

  1. 页面首次渲染
  2. 浏览器窗口大小发生改变
  3. 元素尺寸或位置发生改变
  4. 元素内容变化(文字数量或图片大小等等)
  5. 元素字体大小变化
  6. 添加或者删除可见的DOM元素
  7. 激活CSS伪类(例如::hover)
  8. 查询某些属性或调用某些方法

一些常用且会导致回流的属性和方法:

clientWidth、clientHeight、clientTop、clientLeft
offsetWidth、offsetHeight、offsetTop、offsetLeft
scrollWidth、scrollHeight、scrollTop、scrollLeft
scrollIntoView()、scrollIntoViewIfNeeded()
getComputedStyle()
getBoundingClientRect()
scrollTo()

如何避免回流

  1. 避免频繁操作DOM,创建一个documentFragment,在它上面应用所有DOM操作,最后再把它添加到文档中。
  2. 也可以先为元素设置display: none,操作结束后再把它显示出来。因为在display属性为none的元素上进行的DOM操作不会引发回流和重绘。

博客链接>>

参考资料:
Webkit技术内幕
简述TCP的三次握手过程
HTTP keep-alive详解
浏览器的回流与重绘 (Reflow & Repaint)