一文搞懂页面请求至渲染的过程 (小白版)

564 阅读9分钟

浏览器页面请求至渲染的过程是前端面试的热门问题,考核了面试者对计算器科学方方面面的知识,如操作系统,网络编程和计算器图形学等。网络上有不少对这问题的解答。然而,因为涉及到的知识非常多和杂,看到总是忘了,所以写这篇文章好好整理一下,尽可能写得通俗易懂。

客户端发出请求

首先肯定是客户端发出请求啦,但发出请求的过程会由于软件而不同,用chrome发出,与IE6发出肯定是有不同的,也可能是用curl这种开发工具发出。这里主要说一下用chrome浏览器发出请求,会发生什么。 谈发出请求前,先了解chrome的架构。Chrome虽然是一个浏览器,但其实是由不同的进程组成的,什么是进程,我这里不详细介绍,简单来说,可以理解chrome是一家公司,里面有不同独立的部门,部门之间的通信主要由管道传输,彼此之间不会有任何认识和接触 (都是打工仔)。为什么要独立呢,因为公司业务多而且复杂,所以要独立,好分工,而且不会互相扯皮。既然是打工仔,当然有老板指挥,这个老板就是浏览器进程,他会先看之前有没有去向同一个网址发出请求,如果有,则查看有有没有缓存内容,有则读取,没有则叫网络进程去发请求。 请求是根据http协议编写的,大概内容如下:

  GET /index.html
 HTTP/1.0
  User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5)
  Accept: */*

先不要管内容,为什么我们需要http协议? 问为什么需要,不妨问如果没有http协议会发生什么。 如果全世界只有一个客户端和一台服务器,只要两者制定好通信规范,就可以进行通信。现在问题是,有100个客户端,可能有几十台服务器,如果大家各自用不同规范,岂不是乱套!?所以必须制定一套规范,用于互联网之间的通信。这套规范就是http。 http协议有很多内容,这里只是简单介绍。 http请求有命令,说明这个请求是做什么: get/post/put/delete等,然后有一个网页地址,表示要去什么地方取数据。有一个协议版本,可能是http/1.0, http/1.1, http/2等,还有其他一系列内容。这里不多介绍,详情看书或上网。

DNS

好啦,现在发出去,请求接着去哪儿?它手上有一个地址,但这个地址只是一串字符串 (如 (http://www.google.com)),它不知道具体是哪里。它希望得到的是一个IP地址。IP地址是一连串数字,表示计算器在网络世界的位置。为什么IP地址是数字?因为计算器擅长处理数字,而不是文本,但人类喜欢看文字,所以网址通常是字符串,而不是直接一个IP地址。所以请求得找一个服务器去问,它要做的是去找 DNS。 请求是知道DNS的IP地址,因为客户端预先已经设定了DNS的地址。如果是window用户,可以在命令行输入 ipconfig/all,命令行会输出一系列设定信息,其中包括DNS的设定。Linux则看/etc/resolv.conf的文件。 请求是知道DNS的IP地址,因为客户端预先已经设定了本地DNS的地址。如果是window用户,可以在命令行输入 ipconfig/all,命令行会输出一系列设定信息,其中包括DNS的设定。Linux则看/etc/resolv.conf的文件。

DNS是一个类似于电话簿的服务器,里面存有不少网址的IP地址,如果它不知道,则会问根DNS(根域名服务器)。根DNS是最高层次的,全球共有13套,它不会给出具体的IP地址,但会根据后缀(如 .com,.net),引导本地DNS去问哪一台顶级DNS。

本地DNS转向去访问顶级DNS,后者主要管理二级域名,如.com的DNS管理有 .com后缀的域名。顶级DNS也不直接给IP地址,它引导本地DNS去问权威DNS,最终得到IP地址,本地DNS就把它返回给客户端。

当然,也有可能网址之前就访问过,客户端把IP地址缓存下来,这样直接访问就可以了。

TCP连接

请求拿着它去找服务器。如果到达IP地址的位置,什么都找不到,则会返回http的404响应,表示找不到页面。如果到达且找到服务器,则建立TCP或UDP连接。也有可能到达了发现是一个代理服务器,返回3字头的状态代码和主服务器的IP地址,浏览器需要复位向。 TCP是面向连接的,必须要客户和服务端双方都确定连接,维护连接,数据才可传输。 TCP和UDP连接我不多说了,知识点非常多,三言两语说不清,可参考下面的文章:

Http系列(三) Http/Tcp三次握手和四次挥手

要想系统学习,可订阅 极客时间刘超老师的《趣谈网络协议》

渲染

经历重重难关,终于把服务端传来的http响应包拿到手,大概内容如下:

HTTP/1.1 200 OK
Date: Wed, 02 Sep 2020 02:14:46 GMT
Content-Type: application/javascript
Server: Kestrel
Content-Length: 1476479
Cache-Control: public,max-age=2592000
Last-Modified: Tue, 09 Apr 2019 03:49:50 GMT
Accept-Ranges: bytes
ETag: "1d4ee874822447f"

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> 
        <title>Desk - EDIP-ERP Reporting System</title>
        <base href="/" />

        <link rel="stylesheet" href="/dist/vendor.css?v=qJ-18eTKRuIOxkBcVlEElYR9pHRWHWr38sTzHmaRuCA" />
        
            <link rel="stylesheet" href="/dist/site.css?v=uvQClgv-3Z8KNbFqh_X_ZE5uexudDL31JjBPDPNJUKY" />
        
    </head>
    <body>
        
<div id='app-root'>
    loading...
</div>


        <script src="/dist/vendor.js?v=Io9BFPQEpEWEJ1Q9pnpuMZIFLrV6BKjn3ie18qSGjJ0"></script>
        
    <script src="/dist/main.js?v=yVaGFXJfMb1IBYuoO91EAQN-qeOqMG4lq9pl2tMQAgs"></script>

    </body>
</html>

回应主要分为回应头和内容。响应头存有响应的相关消息,例如内容是什么,从什么地方来,发送时间等。浏览器根据响应头某些内容作相应处理,如content-type为text/html,浏览器作为html文本处理,如果是application/json,作为json处理。 通常面试题里渲染部分略提一下,事实上渲染部分是整个过程的另一个重头戏。通过理解渲染,我们可以更好处理前端页面的优化。本文简单介绍一下渲染过程。 渲染是由渲染流水线进行,这意味着每部分渲染分成模块,每个模块的工作基本可分为输入数据,处理数据和输出数据三大工作。渲染流水线可按顺序分为:

  1. 构建DOM树
  2. 样式计算
  3. 布局阶段
  4. 分层
  5. 绘制
  6. 分块
  7. 光栅化
  8. 合成

构建DOM树

当浏览器得到html文本内容,不是一下子就根据内容进行渲染,而是根据文本节点生成DOM树。所谓html只是w3c所制定的文本规范,具体怎么实现渲染其内容则是浏览器的任务,或你自己也可根据html文本规范写一套软件,不用DOM,另写一种对象来表示html。 DOM树就是我们平时在JS上操作的DOM,我们可以在JS里通过document.getElementbyId等方法获取节点。

样式计算

生成DOM树还是不能立即进行渲染,毕竟页面还得有样式,而不是干巴巴的文本。首先浏览器找到css文本,把文本转换成stylesheet – 浏览器能理解的样式表。还是同样道理,css只是w3c制定的规范而不是实现。 转换成stylesheet后,还要对属性值进行规范处理,即把一些属性值转换为标准值,如 color: yellow转换成 color: rgb(218,227,38),em转为px等。最后按照css的规则设定dom属性的样式。

布局阶段

现在我们的页面有一棵有样式值的DOM树。然而,这棵树是不能直接进行渲染的,因为有些节点不是要渲染的内容,如head或script等,或有些节点的样式值决定它不可见,如display:none。所以浏览器需要筛选出可呈现的节点。把所有筛选出来的节点最后组成布局树。

分层

建了布局树,还需要建立图层树,因为浏覧器的页面是由一系列图层组立而成的。打开 "开发者工具"里的layer (找不到可以在更多工具选项里找),可以看到页面是怎样由图层组成的。

绘制

图层生成后,浏覧器开始建立一系列绘製图层的指令。如果想看绘图指令,可以打开 "开发者工具"里的layer,点击里面的document图层,出现一个detail资料栏,有一个paint profiler,点击它会出现一堆绘图指令。

光栅化

看前面模块的名字大概能推测是做什么的,但光栅化真的不容易让一名前端搞懂,毕竟它是图像学的专有名词。光栅化,英文叫 rasterization,维基百科的介绍如下:

Rasterisation (or rasterization) is the task of taking an image described in a vector graphics format (shapes) and converting it into a raster image (a series of pixels, dots or lines, which, when displayed together, create the image which was represented via shapes).

翻译:光栅化就是把图像视为矢量格式(形状),然后把它转换为光栅图像。光栅图像就是一系列的像素,点或綫,它们组成形状,把图像呈现出来。 看完定义还是摸不着头脑。说白了,就是计算机把图像视为矢量这种数据结构,然后用像素,点或綫等作为"画笔",根据输入的矢量用"画笔"把图像"画"出来。 浏覧器的光栅化就是把用户所看到的视窗区域,以及附近的区域渲染。(光栅化这部分写得简单,实际上複杂多了,但简单来说就是这样)

合成

合成就是把渲染的图像呈现给用户。

以上就是页面请求至渲染过程的简单介绍。