目录
根据之前看的很多文章和做的一点笔记,还是打算整理整理自己做个总结
总的来说,分析的不够深入,可以说是能精简就精简(呜呜呜呜好多资料我也看不懂啊)
唉 就这样吧,等以后水平到位了再来深挖
相关进程
在整个过程中,涉及以下三个进程,三个进程之间交互完成所有工作:
浏览器进程
网络进程
渲染进程
以下是具体步骤
各项准备工作十分繁杂,只是我写的简单:(
一 url解析
在地址栏输入url地址后,该内容首先被地址栏解析,解析的标准如下:
如果输入的地址是一个搜索内容,那么浏览器进程会根据浏览器默认的搜索引擎,来合成新的带关键字的url
判断url是否符合规则,如输入的是www.baidu.com,则地址栏根据规则,会给输入的内容加上协议字段,加上后为https://www.baidu.com
二 发起请求
first,经过解析后的url地址,会被浏览器进程以进程间通信的方式发送给网络进程
1 网络进程收到该http请求后:
(1)首先会检查本地缓存是否存在该资源,如果存在则直接将该资源返回给浏览器进程,那么该次url导航也就结束了
(2)如本地缓存中不存在该资源,网络进程才会正式发起http请求
http请求的具体过程:
i.DNS查询(计网知识,就不详细展开了,主要就是递归与迭代配合的方式进行查询)
ii.DNS查询结束后,获取到了对应ip,这时候就开始建立TCP请求了(如果请求协议是https,在此之前,还要建立TLS连接)
iii. 建立完连接,浏览器端会构建请求行、请求头等信息,如果有相关的cookie,根据实际会将cookie数据添加到请求头中
iv.准备完成,开始发送请求
服务器收到请求后,会根据请求信息生成响应数据,然后发送给网络进程(成功的话)
2 网络进程接收到服务器发来的响应数据,首先解析响应行中的状态码:
(1)如果状态码是301或302,则表示重定向,网络进程根据响应头中的Location字段,(该字段就是重定向的url),重新发起http请求,没错,重新来一遍;
(2)如果状态码是200,那么说明请求资源成功并且成功接收了
(3)成功接收到服务器返回的响应数据,还得判断返回的数据类型,http头中的Content-Type字段会指明返回的响应数据是什么类型;
举例:
'text/plain' :代表这是普通文本,不会产生HTML解析
'text/'html' :代表这是HTML格式内容,就是要进行渲染的所依据的HTML字节流
'application/octet-stream' :代表这是字节流类型,通常浏览器将其视作下载类型处理
如果响应数据被判断为下载类型,那么浏览器会将其交给下载管理器处理,那么这次url导航就结束了;如果是HTML格式的内容,接下来浏览器就该准备渲染工作了
三 准备渲染进程
以Chrome为例,浏览器默认情况下会为每个页面分配一个渲染进程,所以说每打开一个页面,这个页面也就获得了一个渲染进程,当然也有例外;当两个页面从属于一个站点时(同站点即这两个页面的url,协议一样,根域名也一样,那么这两个页面就从属于同一个站点),后打开的页面会复用先打开的页面的渲染进程。
渲染进程准备完毕后,就等着响应数据提交过来(因为现在响应数据还在网络进程里),然后开始渲染工作。
四 提交响应数据
这里又要涉及进程间通信:
1 网络进程收到响应数据后,会告知浏览器进程,然后由浏览器进程来通知渲染进程:数据准备完毕,你可以接收啦!
2 渲染进程收到通知,会和网络进程间建立一条传输数据的“管道”
3 接下来,通过这条“管道”,网络进程和渲染进程完成交接,然后渲染进程会发送给浏览器进程一个“确认接收到响应数据”的消息
4 浏览器进程收到渲染进程发来的确认消息后,会更新浏览器页面状态:包括安全状态、地址栏的url、前进后退的历史状态、更新web页面(这部分之后要着重学习整理下,对,没错,先记着)
然后,就可以开始渲染咯(终于)
BUT!进入渲染这个阶段,不代表渲染是一下就完成的,还要很多渲染前的必备的小(?)事要做。
下面就继续说这些小(?)的事先准备(已经开始逐渐暴躁...)
五 渲染
1 DOM树的构建
说明:渲染引擎内,有一个叫HTML解析器的模块,该模块负责将HTML字节流转换为对应的DOM结构
HTML字节流首先通过分词器以词法分析的方式,被转换为一个个Token,Token包括Tag Token和文本Token,其中Tag Token包括StartTag Token和EndTag Token
说明:HTML解析器维护一个Token栈的结构,该栈用于计算DOM中各节点间的父子关系
通过分词器后转换出来的Token,处理如下:
i. 如果该Token是一个StartTag Token,那么将其压入栈中,同时HTML为该Token创建一个DOM节点,并将该节点加入到DOM树中;
ii. 如果该Token是一个文本Token,则不需入栈,同时HTML为其创建DOM节点并加入DOM树,此时该文本节点的父节点即当前栈顶Token所对应的节点;
iii. 如果该Token是一个EndTag Token,则检查当前栈顶Token是否是与其对应的StartTag Token,是则将栈顶StartTag Token弹出,到此表明一个标签解析完成(比如<div></div>)
词法分析和DOM节点生成就是这么不断进行下去,并且是同步进行的,直到所有的HTML字节流都被解析完成,此时,DOM树也就构建好了。
这里说明一下,这个HTML解析,不是等到渲染进程接收完所有的HTML字节流(也就是网络进程传输给渲染进程的响应数据)才开始的,而是网络进程传多少,渲染进程接收到多少,HTML也就解析多少,这些步骤实际上都是同时进行的。
2 样式计算
DOM树构建好,还不能直接渲染,因为DOM节点对应的样式还没有加进来鸭!(前提是有CSS样式)
计算CSS样式大概分为3步,这3步就不详细展开了,因为比较复杂(自己也不是很懂),大概是下面这样:
i. 将CSS转换为浏览器的能够理解的结构,稍微具体一点就是:渲染引擎接收到CSS文本时,会执行一个转换操作,将CSS文本转换为浏览器可以理解的结构——styleSheets
ii. 转换样式表中的属性值,使其标准化
iii. 计算出DOM树中每个节点的具体样式
3 布局
有了DOM树和计算好了各节点的样式,可以开始计算各节点在页面中的具体位置,也就是怎么排放这些DOM节点。
布局主要分为两步。在网上参考的一些文章,写的是:在布局之前,DOM树和CSS规则树(也就是解析后的CSS)合成渲染树,但是参考了另外的写得更加全面的文章里没有提到渲染树,这里就简单带过一下,本人也不清楚是否生成以及如何生成这个渲染树
稍微详细的步骤如下:
(1)首先创建布局树,这里就要借助刚才构建的DOM树以及计算好的样式,这两者合并生成布局树
(2)布局树和DOM树不同在于:所有的DOM节点在DOM树中是可见的,而布局树中,则忽略掉了那些属性包含display:none的节点,即不可见的节点
(3)构建完布局树,接下来要进行布局计算(布局计算也十分复杂,写不下去了,总之不是我能看懂的 告辞)
4 绘制
网上很多文章,比较笼统,即布局之后就是绘制,(其实我觉得知道这个够了T T,但实际的绘制过程也是超级复杂,我看完详细的文章之后也只懂了一半不到,只能笼统地概括一下(害 前端的知识真的太多了):
上一步说到布局,构建布局树,然后进行布局计算,接下来就是对布局树进行分层,生成图层树
这里简单提一下图层树,不过这又涉及CSS中“层叠上下文”的知识,不展开了。通常情况下,一个DOM节点对应一层,但有的节点没有自己的图层,则从属于其父节点的图层。
满足以下两点其中之一的节点,会被分配到一个图层:
i. 拥有层叠上下文属性的元素;
ii. 需要剪裁的地方也会被创建为图层(比如div中的一段文字,文字占地尺寸超出了div尺寸,那么这时候会发生剪裁,会为文字创建一个新的图层
渲染引擎内部,根据图层树中各个图层,会进行绘制(整个过程精密复杂,知道就好嘻嘻(其实就是我也没看懂)
5 合成显示
渲染引擎完成绘制工作后,这里我觉得是用语问题,因为在这里之前,所说的“绘制”并非绘制在浏览器的页面上,而是相当于第4步的绘制工作是完成了一副画,也就是要展示在页面上的画,并把这幅画交给浏览器进程,告诉浏览器:你把这副画展示在页面上就完事了。(没错,就是这样,我已经神志不清了)
注:浏览器进程首先将这幅画的内容存到内存中,最后再将内存显示在页面上
总结
总结就是,没有总结T T T T T
因为最近做的一个项目,(真是没想到过了好久),我又要重新捡起这部分知识。
(参考了很多文章后,加上我个人的理解,大概地解释了下,对,只是“大概”,因为这个问题涉及的知识实在是太多了!以上应该也只是总结了二分之一不到,要深入的代价是头秃,先不说渲染这个过程有多繁琐,光是所涉及操作系统中进程和线程相关的概念都需要好好整理一遍)