从输入URL到页面渲染完成

1,015 阅读7分钟

从输入URL到页面渲染完成

涉及网络、浏览器工作原理等知识。

前序知识

浏览器进程结构

Browser进程
  负责协调、主控,包括地址栏、书签、历史栈。

GPU进程
  负责整个浏览器界面的渲染

网络进程
  负责发起接收网络请求

插件进程
  控制网页中使用到的插件 如flash
  
渲染器进程
  默认使用(Process-per-site-instance)模式
  四种进程模式:
    Process-per-site-instance: 访问不同站点和同一站点的不同页面都会创建新的进程
    Process-per-site: 同一站点使用同一进程
    Process-per-tab: 一个tab使用一个进程
    Single process: 浏览器引擎和渲染引擎共用一个进程

一、URL

包含:协议名、域名、端口号、路径、查询或其他片段的一个字符串

二、缓存

浏览器在发起请求之前会检查缓存,分别是强缓存和协商缓存

强缓存

浏览器直接判断http头的 Expirescache-control 中的过期时间,其中 Expires 为服务器绝对时间,cache-control为相对时间。

协商缓存

浏览器通过缓存响应头的last-modifiedEtag并在下次请求头中写入改数据,服务器通过判断请求头中数据来决定是否让浏览器使用缓存。
浏览器通过判断 HTTP状态 是否为 304 来判断是否使用本地缓存。

NDS 解析

  1. 检查浏览器DNS缓存
  2. 检查hosts文件
  3. 操作系统查找本地DNS服务器
  4. 本地DNS查找根服务器(本地DNS缓存IP)

TCP

三次握手

用于确认双方收发是否正常

  1. 第一次握手:建立连接。客户端确认是否可以对方 接收 能力
  2. 第二次握手:服务器确认客户端 接收 能力
  3. 第三次握手:客户端确认服务器 发送 能力

HTTP/HTTPS请求

HTTP 请求

请求行 + 请求头 + 请求体 的形式 发送信息

请求行

Method Request-URL HTTP-Version CRLF

请求方式 请求路径 请求版本 换行
GET index.html HTTP/1.1

请求头

  • 请求报头允许客户端向服务器传递请求的附加信息和客户端自身的信息。

  • 常见的请求报头有: Accept, Accept-Charset, Accept-Encoding, Accept-Language, Content-Type, Authorization, Cookie, User-Agent等。

  • Accept用于指定客户端用于接受哪些类型的信息,Accept-Encoding与Accept类似,它用于指定接受的编码方式。Connection设置为Keep-alive用于告诉客户端本次HTTP请求结束之后并不需要关闭TCP连接,这样可以使下次HTTP请求使用相同的TCP通道,节省TCP连接建立的时间。

请求正文

当使用POST, PUT等方法时,通常需要客户端向服务器传递数据。这些数据就储存在请求正文中。在请求包头中有一些与请求正文相关的信息,例如: 请求的数据格式为json。这时就需要设置Content-Type: application/json。

HTTPS 请求

通过非对称加密生成的对称密钥进行的数据加密的方式

  1. 浏览器携带加密方式请求公钥
  2. 服务器返回公钥
  3. 浏览器通过ca检查证书
  4. 浏览器通过公钥+随机数 生成密钥
  5. 服务器通过私钥检查密钥与随机数
  6. 开始通信

响应

通用头部

Request Url: 请求的web服务器地址
 
Request Method: 请求方式
(Get、POST、OPTIONS、PUT、HEAD、DELETE、CONNECT、TRACE)
 
Status Code: 请求的返回状态码,如200代表成功
 
Remote Address: 请求的远程服务器地址(会转为IP)

1xx:指示信息–表示请求已接收,继续处理。

2xx:成功–表示请求已被成功接收、理解、接受。

3xx:重定向–要完成请求必须进行更进一步的操作。

4xx:客户端错误–请求有语法错误或请求无法实现。

5xx:服务器端错误–服务器未能实现合法的请求。

平时遇到比较常见的状态码有:200, 204, 301, 302, 304, 400, 401, 403, 404, 422, 500

响应报头,响应报文

  • 常见的响应报头字段有: Server, Connection...。

  • 服务器返回给浏览器的文本信息,通常HTML, CSS, JS, 图片等文件就放在这一部分。

浏览器解析渲染

  1. 解析html,生成DOM树
  2. 解析CSS,生成CSS规则树
  3. 合成CSS和DOM树,生成Render树
  4. 布局Render树(layout/reflow),负责各元素尺寸、位置的计算
  5. 绘制render树(paint),绘制页面像素信息
  6. 浏览器将各层信息发送给GPU,GPU将各层合并,显示在屏幕上

解析html标签,生成DOM树

通过语法解析将识别后的标签进行dom树构造,并创建document对象,并将document作为DOM树的根节点,并不断添加节点。

当遇到图片和CSS这些网络资源,需要通过网络或者缓存进行加载的资源不会阻止html的解析,因为这些资源并不会影响到DOM的生成。

当遇到script标签,由于浏览器并不知道js是否会改变当前页面的HTML结构( 比如调用了document.write('xxx') 进行修改时当前DOM的解析就变得没有意义了),所以将停止解析DOM转而去加载执行JS资源。

script 并非一定阻塞HTML解析,只要添加 defer 或者 async 属性就可以避免阻塞导致的DOM生成问题。

defer 使得脚本会在dom完整构建之后执行;
async 标签使得脚本只有在完全available才执行,并且是以非阻塞的方式进行的

解析CSS标签,构建CSSOM树

浏览器会把所有的样式解析为样式结构体(包括css样式和浏览器默认样式)

浏览器识别不了的样式不能解析

ps: CSS虽然不会阻塞HTML解析,但是会阻塞渲染(如js先加载会导致后加载的css被阻塞,最后导致页面的白屏也就是阻塞渲染)

生成渲染树

  1. 计算CSS样式

  2. 构建渲染树

  3. 布局(回流,Layout,Reflow),主要定位坐标和大小,是否换行,各种position overflow z-index属性

  4. 绘制(重绘,Repaint),将图像绘制出来

渲染流程

  1. 主线程 生成Layout Tree 和 确认绘制顺序
  2. 主线程 将上述信息传递给合成器线程
  3. 合成器线程将每个图层进行删格化并生成合成器帧
  4. 合成器通过IPC传递给浏览器进程
  5. 浏览器进程将合成器帧传递给GPU并渲染到屏幕上
  6. 当页面发生变化 则重复上述流程

详细流程

浏览器进程中的网络线程将获取到的html通过IPC将数据传递给渲染器主进程,主线程将html解析成DOM树然后进行样式计算,根据DOM树生成好的样式生成Layout Tree,通过遍历Layout Tree 生成渲染顺序表,接着遍历LayoutTree生成LayerTree 然后主线程将LayerTree绘制顺序信息传递给合成器线程,合成器线程将图层传递给删格线程进行删格化,删格化完成后合成器得到删格线程生成的draw quads图块信息,根据这些信息合成器得到一个合成器帧,并通过IPC传递给浏览器进程,浏览器进程传递到GPU进行渲染。

当改变元素尺寸、位置时会重新样式计算,布局(Layout)绘制(Paint)以及后面所有流程,即为重排

当改变元素颜色属性是不会重新触发布局(Layout),但是依然会重新样式计算,即为重绘

回流与重绘

Layout,也称为Reflow,即回流。一般意味着元素的内容、结构、位置或尺寸发生了变化,需要重新计算样式和渲染树

Repaint,即重绘。意味着元素发生的改变只是影响了元素的一些外观之类的时候(例如,背景色,边框颜色,文字颜色等),此时只需要应用新样式绘制这个元素就可以了