【洋小洋同学】一篇没写完的浏览器渲染总结

645 阅读10分钟

一、引言

当打开浏览器,然后在地址栏输入一个网址之后,然后enter,发生了什么?

20200407214919

1. 关键词

  • DNS解析(没有)
  • 304缓存 (没有)
  • TCP协议的三次挥手四次握手(没有)
  • HTTP报文(没有)
  • 重绘、回流
  • 同异步编程(没有)
  • event loop(没有)
  • 宏任务、微任务(没有)
  • 进程、线程
  • 栈内存(没有)
  • 堆内存(没有)
  • 图片懒加载(没有)
  • 压缩(没有)
  • 性能优化
  • 浏览器存储(没有)
  • 缓存机制(没有)

2. 说明

洋小洋同学web 前端高频面试题 不断完善更新

  • 有些资料是参考网上资源,文末有链接
  • 一个浏览器渲染引发的一系列问题
  • 有不正确的欢迎指正

二、进程与线程

1. 进程

程序的一次执行, 它占有一片独有的内存空间.是操作系统执行的基本单元。

  • 一个进程中至少有一个运行的线程: 主线程, 进程启动后自动创建
  • 一个进程中也可以同时运行多个线程, 我们会说程序是多线程运行的
  • 一个进程内的数据可以供其中的多个线程直接共享,多个进程之间的数据是不能直接共享的

(1). 浏览器进程

  • Browser进程: 浏览器的主进程,负责浏览器界面的显示,和各个页面的管理, 浏览器中所有其他类型进程的祖先,负责其他进程的的创建和销毁 它有且只有一个!!!!!
  • Renderer进程: 网页渲染进程,负责页面的渲染,可以有多个 当然渲染进程的数量不一定等于你开打网页的个数
  • 各种插件进程
  • GPU进程 移动设备的浏览器可能不太一样: Android不支持插件,所以就没有插件进程 GPU演化成了Browser进程的一个线程 Renderer进程演化成了操作系统的一个服务进程,它仍然是独立的

2 .线程

是进程内的一个独立执行单元,是CPU调度的最小单元。程序运行的基本单元 线程池(thread pool): 保存多个线程对象的容器, 实现线程对象的反复利用

由于jS是单线程的,就牵扯到事件循环、事件轮询、event loop (省略先)

三、HTTP 请求

1. DNS解析

2 .TCP协议的三次握手四次挥手

3 .HTTPS和HTTP

四、HTTP 响应

五、浏览器渲染原理

20200407221502

1. 浏览器功能

  • 网络

    • 浏览器通过网络模块来下载各式各样的资源,例如html文本;javascript代码;样式表;图片;音视频文件等。
    • 网络部分本质上十分重要,因为它耗时长,而且需要安全访问互联网上的资源。
  • 资源管理

    • 从网络下载,或者本地获取到的资源需要有高效的机制来管理它们。
    • 例如如何避免重复下载,资源如何缓存等
  • 网页浏览

    • 资源转为可视化
  • 等等……

2. 浏览器内核

参考阅读百度百科

浏览器最重要或者说核心的部分是“Rendering Engine”,可大概译为“渲染引擎”,不过我们一般习惯将之称为“浏览器内核”。

不同的浏览器内核对网页编写语法的解释也有不同,因此同一网页在不同的内核的浏览器里的渲染(显示)效果也可能不同,这也是网页编写者需要在不同内核的浏览器中测试网页显示效果的原因。

内核分类 采用该内核的浏览器
Trident(IE IE、傲游、世界之窗浏览器、Avant、腾讯TT、Sleipnir、GOSURF、GreenBrowser和KKman等。
Gecko(火狐) [Mozilla Firefox](baike.baidu.com/item/Mozill… Firefox)、Mozilla SeaMonkey、waterfox(Firefox的64位开源版)、Iceweasel、Epiphany(早期版本)、Flock(早期版本)、K-Meleon。
Webkit(谷歌 Google Chrome、360极速浏览器以及搜狗高速浏览器高速模式也使用Webkit作为内核
Blink
Presto

3. 浏览器渲染机制

  • 浏览器采用流式布局模型(Flow Based Layout
  • 浏览器会把HTML解析成DOM,把CSS解析成CSSOMDOMCSSOM合并就产生了渲染树(Render Tree)。
  • 有了RenderTree,我们就知道了所有节点的样式,然后计算他们在页面上的大小和位置,最后把节点绘制到页面上。
  • 由于浏览器使用流式布局,对Render Tree的计算通常只需要遍历一次就可以完成,table及其内部元素除外,他们可能需要多次计算,通常要花3倍于同等元素的时间,这也是为什么要避免使用table布局的原因之一

4. 浏览器渲染引擎

(1). 主要模块

  • HTML解析器

    • 解释HTML文档的解析器
    • 作用:将HTML文本解释成DOM树
  • CSS解析器

    • 它的作用是为DOM中的各个元素对象计算出样式信息
    • 为布局提供基础设施
  • JavaScript引擎

    • 使用Javascript代码可以修改网页的内容,也能修改css的信息
    • javascript引擎能够解释javascript代码,并通过DOM接口和CSS树接口来修改网页内容和样式信息,从而改变渲染的结果
  • 布局(layout)回流

    • 在DOM创建之后,Webkit需要将其中的元素对象同样式信息结合起来
    • 计算他们的大小位置等布局信息
    • 形成一个能表达这所有信息的内部表示模型
  • 绘图模块

    • 使用图形库将布局计算后的各个网页的节点绘制成图像结果

(2). 渲染过程

渲染树的构建、布局、及绘制中文

20200407223600

  1. 遇见 HTML 标记,调用HTML解析器解析为对应的 token (一个token就是一个标签文本的序列化)并构建 DOM 树(就是一块内存,保存着tokens,建立它们之间的关系)
  2. 遇见 style/link 标记 调用解析器 处理 CSS 标记并构建 CSS样式树,即CSSOM
  3. 遇见 script 标记 调用 javascript解析器 处理script标记,绑定事件、修改DOM树/CSS树 等
  4. 将 DOM树 与 CSS树 合并成一个渲染树
  5. 根据渲染树来渲染,以计算每个节点的几何信息(这一过程需要依赖图形库)
  6. 将各个节点绘制到屏幕上。

5 .浏览器渲染阻塞

(1). CSS样式渲染阻塞

link引入的外部css才能够产生阻塞

(2). JS阻塞

六、性能优化

1. 减少HTTP的请求

2. 减少DOM的重绘与回流

(1). 重绘

由于节点的几何属性发生改变或者由于样式发生改变而不会影响布局的,称为重绘,例如outline, visibility, colorbackground-color等,重绘的代价是高昂的,因为浏览器必须验证DOM树上其他节点元素的可见性。

(2). 回流

回流是布局或者几何属性需要改变就称为回流。回流是影响浏览器性能的关键因素,因为其变化涉及到部分页面(或是整个页面)的布局更新。一个元素的回流可能会导致了其所有子元素以及DOM中紧随其后的节点、祖先节点元素的随后的回流。有的大佬也习惯称之为重排

什么情况下浏览器会发生回流
  • 添加或删除可见的DOM元素
  • 元素的位置发生变化
  • 元素的尺寸发生变化(包括外边距、内边框、边框大小、高度和宽度等)
  • 内容发生变化,比如文本变化或图片被另一个不同尺寸的图片所替代。
  • 页面一开始渲染的时候(这肯定避免不了)
  • 浏览器的窗口尺寸变化(因为回流是根据视口的大小来计算元素的位置和大小的)

(3). 浏览器优化

现代浏览器大多都是通过队列机制来批量更新布局,浏览器会把修改操作放在队列中,至少一个浏览器刷新(即16.6ms)才会清空队列,但当你获取布局信息的时候,队列中可能有会影响这些属性或方法返回值的操作,即使没有,浏览器也会强制清空队列,触发回流与重绘来确保返回正确的值

主要包括以下属性或方法:

  • offsetTopoffsetLeftoffsetWidthoffsetHeight
  • scrollTopscrollLeftscrollWidthscrollHeight
  • clientTopclientLeftclientWidthclientHeight
  • widthheight
  • getComputedStyle()
  • getBoundingClientRect()

(4). 小结

回流必定会发生重绘,重绘不一定会引发回流 怎么最小化重绘重排?

  1. CSS
    • 使用 transform 替代 top

    • 使用 visibility 替换 display: none ,因为前者只会引起重绘,后者会引发回流(改变了布局

    • 避免使用table布局,可能很小的一个小改动会造成整个 table 的重新布局。

    • 尽可能在DOM树的最末端改变class,回流是不可避免的,但可以减少其影响。尽可能在DOM树的最末端改变class,可以限制了回流的范围,使其影响尽可能少的节点。

    • 避免设置多层内联样式,CSS 选择符从右往左匹配查找,避免节点层级过多。

      <div>
        <a> <span></span> </a>
      </div>
      <style>
        span {
          color: red;
        }
        div > a > span {
          color: red;
        }
      </style>
      

      对于第一种设置样式的方式来说,浏览器只需要找到页面中所有的 span 标签然后设置颜色,但是对于第二种设置样式的方式来说,浏览器首先需要找到所有的 span 标签,然后找到 span 标签上的 a 标签,最后再去找到 div 标签,然后给符合这种条件的 span 标签设置颜色,这样的递归过程就很复杂。所以我们应该尽可能的避免写过于具体的 CSS 选择器,然后对于 HTML 来说也尽量少的添加无意义标签,保证层级扁平

    • 将动画效果应用到position属性为absolutefixed的元素上,避免影响其他元素的布局,这样只是一个重绘,而不是回流,同时,控制动画速度可以选择 requestAnimationFrame,详见探讨 requestAnimationFrame

    • 避免使用CSS表达式,可能会引发回流。

    • 将频繁重绘或者回流的节点设置为图层,图层能够阻止该节点的渲染行为影响别的节点,例如will-changevideoiframe等标签,浏览器会自动将该节点变为图层。

    • CSS3 硬件加速(GPU加速),使用css3硬件加速,可以让transformopacityfilters这些动画不会引起回流重绘 。但是对于动画的其它属性,比如background-color这些,还是会引起回流重绘的,不过它还是可以提升这些动画的性能。

  2. JavaScript
    • 避免频繁操作样式,最好一次性重写style属性,或者将样式列表定义为class并一次性更改class属性。
    • 避免频繁操作DOM,创建一个documentFragment,在它上面应用所有DOM操作,最后再把它添加到文档中。
    • 避免频繁读取会引发回流/重绘的属性,如果确实需要多次使用,就用一个变量缓存起来。
    • 对具有复杂动画的元素使用绝对定位,使它脱离文档流,否则会引起父元素及后续元素频繁回流。

七、思考

问题一:为什么在请求的阶段需要三次握手,断开连接的时候要四次挥手?

问题二:当下的前端框架如此流行的原因,单单是因为好用开发效率高嘛?jQuery时代是怎么样的一个时代?当下为啥很少用

问题三:HTTP与HTTPS有什么不同,HTTPS底层的原理是什么?

八、参考阅读