谈谈浏览器的渲染和优化

浏览器的渲染

浏览器是多进程的,但是有一个主进程Browser进程,Render渲染进程,GPU进程,第三方插件进程等。


Broswer进程

浏览器的主进程(负责协调、主控)
负责浏览器界面显示,与用户交互
负责各个页面的管理,创建和销毁其他进程
将Renderer进程得到的内存中的Bitmap,绘制到用户界面上 网络资源的管理,下载等

示例:pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。

Render进程

渲染进程有多个线程

GUI渲染线程

负责渲染浏览器页面,解析html 和css,构建dom 和 objectRender树
当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行
GUI渲染线程与JS引擎线程是互斥的,当JS引擎执行时GUI线程会被挂起

JS引擎线程

也称为JS内核,负责处理Javascript脚本程序。
JS引擎线程负责解析Javascript脚本,运行代码。
JS引擎一直等待着任务队列中任务的到来,然后加以处理,一个Tab页(renderer进程)中无论什么时候都只有一个JS线程在运行JS程序
同样注意,GUI渲染线程与JS引擎线程是互斥的,所以如果JS执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞。

事件触发线程

归属于浏览器而不是JS引擎,用来控制事件循环
当JS引擎执行代码块如setTimeOut时(也可来自浏览器内核的其他线程,如鼠标点击、AJAX异步请求等),会将对应任务添加到事件线程中
当对应的事件符合触发条件被触发时,该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理
注意,由于JS的单线程关系,所以这些待处理队列中的事件都得排队等待JS引擎处理(当JS引擎空闲时才会去执行)

定时器线程

传说中的setInterval与setTimeout所在线程
浏览器定时计数器并不是由JavaScript引擎计数的,(因为JavaScript引擎是单线程的, 如果处于阻塞线程状态就会影响记计时的准确)
因此通过单独线程来计时并触发定时(计时完毕后,添加到事件队列中,等待JS引擎空闲后执行)
W3C在HTML标准中规定,规定要求setTimeout中低于4ms的时间间隔算为4ms。

http异步线程

在XMLHttpRequest在连接后是通过浏览器新开一个线程请求
将检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件,将这个回调再放入事件队列中。再由JavaScript引擎执行。

渲染进程中多个线程之间的关系

GUI渲染线程与JS引擎线程互斥
JS阻塞页面加载
WebWorker:

创建Worker时,JS引擎向浏览器申请开一个子线程(子线程是浏览器开的,完全受主线程控制,而且不能操作DOM)
JS引擎线程与worker线程间通过特定的方式通信(postMessage API,需要通过序列化对象来与线程交互特定的数据)
如果有非常耗时的工作,请单独开一个Worker线程,这样里面不管如何翻天覆地都不会影响JS引擎主线程,
JS引擎是单线程的,这一点的本质仍然未改变,Worker可以理解是浏览器给JS引擎开的外挂,专门用来解决那些大量计算问题。

SharedWorker:

SharedWorker是浏览器所有页面共享的,不能采用与Worker同样的方式实现,因为它不隶属于某个Render进程,可以为多个Render进程共享使用
所以Chrome浏览器为SharedWorker单独创建一个进程来运行JavaScript程序,在浏览器中每个相同的JavaScript只存在一个SharedWorker进程,不管它被创建多少次。

Browser进程和浏览器内核(Renderer进程)的通信过程

Browser进程收到用户请求,首先需要获取页面内容(譬如通过网络下载资源),随后将该任务通过RendererHost接口传递给Render进程

Renderer进程的Renderer接口收到消息,简单解释后,交给渲染线程,然后开始渲染

渲染线程接收请求,加载网页并渲染网页,这其中可能需要Browser进程获取资源和需要GPU进程来帮助渲染
当然可能会有JS线程操作DOM(这样可能会造成回流并重绘) 最后Render进程将结果传递给Browser进程

Browser进程接收到结果并将结果绘制出来

浏览器渲染流程

浏览器输入url,浏览器主进程接管,开一个下载线程,
然后进行 http请求(略去DNS查询,IP寻址等等操作),然后等待响应,获取内容,
随后将内容通过RendererHost接口转交给Renderer进程

浏览器渲染流程开始:

  • 解析html建立dom树

css加载不会阻塞DOM树解析(异步加载时DOM照常构建)
但会阻塞render树渲染(渲染时需等css加载完毕,因为render树需要css信息)

  • 解析css构建css规则树(将CSS代码解析成树形的数据结构,然后结合DOM合并成render树)
  • 布局render树(Layout/reflow),负责各元素尺寸、位置的计算(计算布局)
  • 绘制render树(paint),绘制页面像素信息
  • 浏览器会将各层的信息发送给GPU,GPU会将各层合成(composite),显示在屏幕上。

渲染完毕后就是load事件了 load事件与DOMContentLoaded事件的先后: 当 DOMContentLoaded
事件触发时,仅当DOM加载完成,不包括样式表,图片 当 onload 事件触发时,页面上所有的DOM,样式表,脚本,图片都已经加载完成了。
DOMContentLoaded -> load

普通图层和复合图层 GPU中,各个复合图层是单独绘制的,所以互不影响,这也是为什么某些场景硬件加速效果一级棒 如何变成复合图层(硬件加速):
1.translate3d、translateZ
2.opacity属性/过渡动画(需要动画执行的过程中才会创建合成层,动画没有开始或结束后元素还会回到之前的状态)
3.will-chang属性(提前预告),一般配合opacity与translate使用
4.等元素
5.使用硬件加速时,尽可能的使用index,防止浏览器默认给后续的元素创建复合层渲染

浏览器的渲染优化

通过performance去分析性能以及性能的优化
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
【JS】

JavaScript 计算,特别是会触发大量视觉变化的计算会降低应用性能。 不要让时机不当或长时间运行的 JavaScript 影响用户交互

下面是一些常见 JavaScript 问题

1、大开销输入处理程序影响响应或动画

让浏览器尽可能晚地处理触摸和滚动,或者绑定侦听

2、时机不当的 JavaScript 影响响应、动画、加载

使用 requestAnimationFrame、使 DOM 操作遍布各个帧、使用网络工作线程

3、长时间运行的 JavaScript 影响响应

将纯粹的计算工作转移到web worker中。如果需要 DOM 访问权限,配合使用requestAnimationFrame

【样式】

样式更改开销较大,在这些更改会影响 DOM 中的多个元素时更是如此。 只要将样式应用到元素,浏览器就必须确定对所有相关元素的影响、重新计算布局并重新绘制

点击 Recalculate Style 事件(以紫色显示)可以在 Details 窗格中查看更多相关信息。 如果样式更改需要较长时间,对性能的影响会非常大。 如果样式计算会影响大量元素,则需要改进另一个方面。
  要降低 Recalculate Style 事件的影响,使用一些对渲染性能的影响较小的属性。如使用 transform 和 opacity 属性更改来实现动画,使用 will-change 或 translateZ 提升移动的元素

下面是一些常见的CSS问题

1、大开销样式计算影响响应或动画

任何会更改元素几何形状的 CSS 属性,如宽度、高度或位置;浏览器必须检查所有其他元素并重做布局。避免会触发重排的CSS属性

2、复杂的选择器影响响应或动画

嵌套选择器强制浏览器了解与所有其他元素有关的全部内容,包括父级和子级。尽量在CSS中引用只有一个类的元素

【重排】

布局(或重排)是浏览器用来计算页面上所有元素的位置和大小的过程。 网页的布局模式意味着一个元素可能影响其他元素;例如body元素的宽度一般会影响其子元素的宽度以及树中各处的节点等等。这个过程对于浏览器来说可能很复杂。 一般的经验法则是,如果在帧完成前从 DOM 请求返回几何值,将发现会出现“强制同步布局”,在频繁地重复或针对较大的 DOM 树执行操作时这会成为性能的大瓶颈。

performance面板可以确定页面何时会导致强制同步布局。 这些 Layout 事件使用红色竖线标记
   “布局抖动”是指反复出现强制同步布局情况。 这种情况会在 JavaScript 从 DOM 反复地写入和读取时出现,将会强制浏览器反复重新计算布局

避免强制同步布局和布局抖动;先读取样式值,然后进行样式更改。 首先 JavaScript 运行,然后计算样式,然后布局。但是,可以使用
JavaScript 强制浏览器提前执行布局。这被称为强制同步布局。

当在页面中不指定img的width,height属性值时,常会出现页面布局的抖动现象。 要养成为图像指定 height 和 width
属性的好习惯。同时不建议通过 height 和 width 属性来缩放图像。

避免布局抖动
有一种方式会使强制同步布局甚至更糟:接二连三地执行大量这种布局。看看这个代码:

function resizeAllParagraphsToMatchBlockWidth() {

  // Puts the browser into a read-write-read-write cycle.
  for (var i = 0; i < paragraphs.length; i++) {
    paragraphs[i].style.width = box.offsetWidth + 'px';
  }
}

【重绘】

绘制是填充像素的过程。这经常是渲染流程开销最大的部分。 如果在任何情况下注意到页面出现卡顿现象,很有可能存在绘制问题。

合成是将页面的已绘制部分放在一起以在屏幕上显示的过程。 大多数情况下,如果坚持仅合成器属性并避免一起绘制,性能会有极大的改进,但是需要留意过多的层计数

一定不要使用下面的代码

* { will-change: transform; transform: translateZ(0); }

这是以迂回方式说想要提升页面上的每个元素。此处的问题是创建的每一层都需要内存和管理,而这些并不是免费的。事实上,在内存有限的设备上,对性能的影响可能远远超过创建层带来的任何好处。每一层的纹理都需要上传到 GPU,使 CPU 与 GPU 之间的带宽、GPU 上可用于纹理处理的内存都受到进一步限制

如果大部分渲染时间花费在绘制上,即表示存在绘制问题

下面是一些常见的绘制问题

1、绘制风暴影响响应或动画

较大的绘制区域或大开销绘制影响了响应或动画,要避免绘制、提升将要移动到自有层的元素,使用变形和不透明度

2、层数激增影响动画

使用 translateZ(0) 过度提升过多的元素会严重影响动画性能,要谨慎提升到层,并且仅在了解这样会有切实改进时才提升到层
服务器渲染
服务器端渲染主要有两个优势,一是加快首屏渲染时间,二是有利于SEO。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
图一为客户端渲染流程图,图二,三为服务器端渲染流程图。

两个渲染图都可以分为两个阶段:
一:客户端发送请求,服务器端返回html文件。
二:客户端请求js文件,下载完成后本地建立react实例。
第一阶段结束时,服务器端返回渲染结果,用户即可看到首屏。而对于客户端渲染,需要等待一次脚本下载时间,以及在客户端的渲染时间。由于客户端的硬件以及网络条件的差异,这两段时间开销可能十分显著。

总结

主要是从浏览器的渲染原理讲起,包括每个流程所对应的职责,渲染优化主要是从原理出发并且结合分析工具,分析具体原因,再找解决方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值