步骤拆分
- 网络-请求和响应
- 缓存
- DNS解析
- 建立TCP连接(三次握手和四次挥手)
- 服务端验证请求
- 响应文件类型(Content-Type)
- 浏览器-解析与渲染
- 浏览器进程和线程
- 构建Dom树
- layout paint 合成显示
详细回答
一、缓存
- 缓存在请求资源时很大程度提高了效率,对于前端和服务端都是很好的性能优化的手段。
- 缓存分为强缓存和协商缓存
- 判断强缓存的条件是,在请求开始,浏览器会验证该请求是否具有强缓存,判定缓存的标识是请求头中的cache-control(
http1.1同时存在cache-control和expires
)、expires(http1.0只存在expires
)字段,当两者在http1.1同时设置时,优先使用cache-control判断是否具有强缓存 - cache-control的值是max-age = xx;这个值代表客户端收到第一次响应成功后,该资源请求需要被缓存的时间,未超过这个时间下次请求则不发送任何请求,直接使用缓存数据
- 判断协商缓存的条件是请示头中的
If-None-Match
和响应头中的E-tag
,这两个字段成对存在,如果发出请求后两者相对于,则表示请求结果未发生任何改变,浏览器可以使用本地缓存,服务器返回状态码304
二、DNS解析
- DNS的解析过程是域名转换为真实IP地址的过程。
- 一个域名对应一个IP地址,一个IP地址可以对应多个域名。
- 优先从本机hosts文件中查找是否有和这个网站和IP地址映射关系,有则直接直接使用这个IP地址,完整解析。
- hosts文件中没有映射关系,DNS会按照固定的顺序查找缓存,步骤为
浏览器
、本机系统
、路由器缓存
、运营商缓存
、根域名服务器
、顶级域名服务器
、主域名服务器
。 - 没有缓存则像DNS服务器发出请求,获取该域名对应的真实IP地址(此过程可以使用
dns-prefetch
进行预解析优化)
三、TCP连接
- http的本质就是TCP连接,此时要进行三次握手。
- 第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SENT状态,等待服务器确认。
- 第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
- 客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。
- 该过程完成后,服务端开始像客户端传输数据。
四、服务端验证请求
- 服务端收到请求会对请求进行统一的验证,如安全拦截,跨域验证等。
- 根据请求类型做出处理,返回对应的响应白报文。
五、响应文件类型
- 服务端会根据Content-Type 来判断响应文件类型
- 文本、图片等资源浏览器会直接渲染展示到页面
- stream流文件,浏览器会进行下载
- html文档,会交给浏览器进行解析和渲染
六、浏览器的线程和进程
- 浏览器是多进程的,每一个窗口是一个进程。浏览器内核则是多线程的,资源下载会进行多线程下载。
- 浏览器内核的线程分为以下几种
- 定时器线程
- 负责执行异步定时器一类的函数的线程,如 setInterval,setTimeout 等。
- 主线程依次执行代码时,遇到定时器,会将定时器交给该线程处理,当计数完毕后, 事件触发线程会将计数完毕的事件加入到任务队列的尾部,等待 JS引擎线程执行。
- GUI线程
- 主要负责页面的渲染,解析 HTML,CSS,构建 DOM 树,布局和绘制等。
- 当页面需要重绘或者由于某种操作引发回流时,将执行该线程。
- 该线程与JS 引擎线程互斥,当执行 JS 引擎线程时,GUI渲染线程会被挂起, 当前任务队列为空时,JS引擎才会去执行 GUI 渲染。
- 网络请求进程
- 主要负责异步请求一类的函数的线程,如 Promise,axios,ajax等,主线程依次执行代码时,遇到异步请求,会将函数交给该线程处理,当监听到状态码变更,如果有回调函数,时间触发线程会将回调函数加入到任务队列的尾部,等待 JS 引擎线程的执行。
- Js引擎线程
- 主要负责处理 JavaScript 脚本,执行代码。
- 也主要负责执行准备好的待执行的事件,即定时器结束,或者异步请求成功并正确返回时,将依次进入任务 队列,等待 JS 引擎线程的执行。
- 该线程与 GUI 渲染线程互斥,当 JS引擎线程执行 JavaScript 脚本时间过长, 将导致页面渲染的阻塞。
- 事件触发线程
- 主要负责将准备好的事件交给 JS 引擎线程执行,比如 setTimeout 定时器计数结束,ajax 等异步请求成功并触发回调函数,或者用户触发点击事件时,该线程会将整装待发的事件依次加入到任务队列的尾部,等待 JS 引擎线程的执行。
- 定时器线程
- 浏览器内核得到报文后会进行以下的步骤完成页面的渲染
- 解析HTML得到Dom Tree
- 解析Css 得到Css Tree
- 合并Dom Tree和Css Tree生成Render Tree
- layout(布局) 计算每个元素的大小和位置,将元素布局到页面正确的位置
- paint(绘制) 绘制页面的像素信息,填充具体的样式
- GPU将合成后的结果显示到页面
七、构建Dom树的过程
构建Dom是编码和解码的过程,主要经过一下几个环节
- 编码: 将html原始字节内容转换为指定的编码内容
- 令牌化: 每一个标签都是一个令牌,内部保存自己的规则,令牌记录了标签的开始和结束,这也是判断标签有没有子节点的依据
- 生成对象:每一个令牌都会生成具有属性的对象,该对象保存该节点所携带的信息
- 构建完成 : 形成多个对象组成的树形结构
注: CssTree的构建过程同理
DomTree和CssTree最终合并为RenderTree,此时浏览器可以进行渲染
八、layout和paint
- 根据渲染树中的每一个节点位置,尺寸信息,将节点渲染到执行的位置
- 根据渲染树中节点的样式信息,为每一个节点分配样式
九、回流和重绘
- 回流: 当元素的位置,大小,结构等信息发生变化时,会构建渲染树,此过程称为回流。
- 重绘: 当元素外观视觉发生变化时,只需要使用新的样式覆盖这个元素,此过程称为重绘。
- 回流必定引起重绘,重绘不一定引起回流。