如何提升组织效率WEB页面渲染效率

一直都听说DOM很慢要尽量少的去操作DOM,于是就想进一步去探究下为什么大家都会这样说在网上学习了一些资料,这边整理出来

  首先,DOM对象本身也是一个js对象所鉯严格来说,并不是操作这个对象慢而是说操作了这个对象后,会触发一些浏览器行为比如布局(layout)和绘制(paint)。下面主要先介绍下這些浏览器行为阐述一个页面是怎么最终被呈现出来的,另外还会从代码的角度来说明一些不好的实践以及一些优化方案。

  浏览器是如何呈现一张页面的

  一个浏览器有许多模块其中负责呈现页面的是渲染引擎模块,比较熟悉的有WebKit和Gecko等这里也只会涉及这个模塊的内容。

  先用文字大致阐述下这个过程:

  • 对Render tree的各个节点计算布局信息比如box的位置与尺寸
  • 根据Render tree并利用浏览器的UI层进行绘制

  上图昰Webkit的基本流程,在术语上和Gecko可能会有不同这里贴上Gecko的流程图,不过文章下面的内容都会统一使用Webkit的术语

  影响页面呈现的因素有许哆,比如link的位置会影响首屏呈现等但这里主要集中讨论与layout相关的内容。

  paint是一个耗时的过程然而layout是一个更耗时的过程,我们无法确萣layout一定是自上而下或是自下而上进行的甚至一次layout会牵涉到整个文档布局的重新计算。

  但是layout是肯定无法避免的所以我们主要是要最尛化layout的次数。

  什么情况下浏览器会进行layout

  在考虑如何最小化layout次数之前要先了解什么时候浏览器会进行layout。

  layout(reflow)一般被称为布局这个操作是用来计算文档中元素的位置和大小,是渲染前重要的一步在HTML第一次被加载的时候,会有一次layout之外js脚本的执行和样式的改變同样会导致浏览器执行layout,这也是本文的主要要讨论的内容

  一般情况下,浏览器的layout是lazy的也就是说:在js脚本执行时,是不会去更新DOM嘚任何对DOM的修改都会被暂存在一个队列中,在当前js的执行上下文完成执行后会根据这个队列中的修改,进行一次layout

  然而有时希望茬js代码中立刻获取最新的DOM节点信息,浏览器就不得不提前执行layout这是导致DOM性能问题的主因。

  如下的操作会打破常规并触发浏览器执荇layout:

  • 通过js获取需要计算的DOM属性
  • resize浏览器窗口大小
  • 通过js修改DOM元素样式且该样式涉及到尺寸的改变

  我们来通过一个例子直观的感受下:

  clientHeight,这个属性是需要计算得到的于是就会触发浏览器的一次layout。我们来利用chrome(v47.0)的开发者工具看下(截图中的timeline record已经经过筛选仅显示layout):

  上面的例子中,代码首先修改了一个元素的样式接下来读取另一个元素的clientHeight属性,由于之前的修改导致当前DOM被标记为脏为了保证能准確的获取这个属性,浏览器会进行一次layout(我们发现chrome的开发者工具良心的提示了我们这个性能问题)

  优化这段代码很简单,预先读取所需要的属性在一起修改即可。

  下面再介绍一些其他的优化方案

  最小化layout的方案

  上面提到的一个批量读写是一个,主要是洇为获取一个需要计算的属性值导致的那么哪些值是需要计算的呢?

  这个链接里有介绍大部分需要计算的属性:

  再来看看别的凊况:

  面对一系列DOM操作

  针对一系列DOM操作(DOM元素的增删改)可以有如下方案:

  这类优化方案的核心思想都是相同的,就是先對一个不在Render tree上的节点进行一系列操作再把这个节点添加回Render tree,这样无论多么复杂的DOM操作最终都只会触发一次layout。

  针对样式的改变我們首先需要知道并不是所有样式的修改都会触发layout,因为我们知道layout的工作是计算RenderObject的尺寸和大小信息那么我如果只是改变一个颜色,是不会觸发layout的

  这里有一个网站,详细列出了各个CSS属性对浏览器执行layout和paint的影响

  像下面这种情况,和上面讲优化的部分是一样的注意丅读写即可。

  但是要提一下动画这边讲的是js动画,比如:

  动画的每一帧都会导致layout这是无法避免的,但是为了减少动画带来的layout嘚性能损失可以将动画元素绝对定位,这样动画元素脱离文本流layout的计算量会减少很多。

  在现实项目中代码按模块划分,很难像仩例那样组织批量读写那么这时可以把写操作放在requestAnimationFrame的callback中,统一让写操作在下一次paint之前执行

  可以很清楚的观察到Animation Frame触发的时机,MDN上说昰在paint之前触发不过我估计是在js脚本交出控制权给浏览器进行DOM的invalidated check之前执行。

  除了由于触发了layout而导致性能问题外这边再列出一些其他細节:

  缓存选择器的结果,减少DOM查询这里要特别提下HTMLCollection。HTMLCollection是通过document.getElementByTagName得到的对象类型和数组类型很类似但是每次获取这个对象的一个属性,都相当于进行一次DOM查询:

  比如上面的这段代码会导致无限循环所以处理HTMLCollection对象的时候要做些缓存。

  另外减少DOM元素的嵌套深喥并优化css,去除无用的样式对减少layout的计算量有一定帮助

  在DOM查询时,querySelectorquerySelectorAll应该是最后的选择它们功能最强大,但执行效率很差如果鈳以的话,尽量用其他方法替代

  下面两个jsperf的链接,可以对比下性能

  自己对View层的想法

  上面的内容理论方面的东西偏多,从實践的角度来看上面讨论的内容,正好是View层需要处理的事情已经有一个库FastDOM来做这个事情,不过它的代码是这样的:

  问题很明显會导致callback hell,并且也可以预见到像FastDOM这样的imperative的代码缺乏扩展性关键在于用了requestAnimationFrame后就变成了异步编程的问题了。要让读写状态同步那必然需要在DOM嘚基础上写个Wrapper来内部控制异步读写,不过都到了这份上感觉可以考虑直接上了......

  总之,尽量注意避免上面说到的问题但如果用库,仳如的话layout的问题出在库本身的抽象上。像React引入自己的组件模型用过virtual DOM来减少DOM操作,并可以在每次state改变时仅有一次layout我不知道内部有没有鼡requestAnimationFrame之类的,感觉要做好一个View层就挺有难度的之后准备学学React的代码。希望自己一两年后会过来再看这个问题的时候可以有些新的见解。

}

一个网页通常可以包含很多层(该層并不完全等同于RenderLayer)例如有透明效果的节点, Canvas节点等这些节点都可以是页面中的一层,这些层的内容最后组成一个可视化的网页内容洳下图所示。


在这里稍微解释一下Layer的概念我们都知道WebCore中的三棵树:DOM树,Render树及RenderLayer树事实上远不止这三棵树,在开启硬件加速的情况下WebView会構成一棵与RenderLayer树结构并行的Layer树,通常RenderLayer树中的一个或多个节点对于Layer树中的一个节点这才是上图中的Layer的含义。

在没有硬件加速的情况下浏览器通常是依赖于CPU来渲染生成网页的内容,大致的做法是遍历这些层然后按照顺序把这些层的内容依次绘制在一个内部存储空间上(例如bitmap),最后把这个内部表示显示出来这种做法就是软件渲染(softwarerendering)。

随着GPU硬件能力的增强包括在很多小型设备上也是如此,浏览器可以借助于其处理图形方面的性能来对渲染实现加速此时不再将所有层绘制到一起,而是进行分层渲染合成之后再显示到屏幕上。

整个渲染過程基本上可以分为相对独立的三个步骤:

下面详细介绍这三个步骤

该步骤即WebCore更新自己的树结构的过程,在这里不在赘述

开启硬件加速后,WebKit有几个重要的类为硬件加速提供支持在这里首先介绍一下,它们的具体作用在下文中会有详细介绍

几个主要类的关系图如下所礻:


}

网站最基本的东西是什么

――內容?SEO(搜索引擎优化)UE(用户体验)?都不对!是速度!

内容再丰富的网站如果慢到无法访问也是毫无意义的; SEO做的再好的网站,洳果搜索蜘蛛抓不到也是白搭; UE设计的再人性化的网站如果用户连看都看不到也是空谈。

所以网页的效率绝对是最值得关注的方面如哬才能提高一个网页的效率呢?Steve Souders(Steve Souders的资料/pub/au/2951)提出的提高网页效率的14条准则而这些准则也将是我们下篇中介绍到的YSlow工具的理论基础:

这里我们將逐一的讲解这些准则,对其中开发者密切相关的准则我将详细讲解。小弟个人技术实在有限错误和无知在所难免,还请高人指点


80%的用戶响应时间都是浪费在前端。而这些时间主要又是因为下载图片、样式表、JavaScript脚本、flash等文件造成的减少这些资源文件的Request请求数将是提高网頁显示效率的重点。
这里好像有个矛盾就是如果我减少了很多的图片,样式脚本或者flash,那么网页岂不是光秃秃的那多难看呢?其实這是一个误解我们只是说尽量的减少,并没有说完全不能使用减少这些文件的Request请求数,当然也有一些技巧和建议的:

1:用一个大图片玳替多个小图片

这的确有点颠覆传统的思维了。以前我们一直以为多个小图片的下载速度之和会小于一个大图片的下载速度但是现在利用httpwatch工具的对多个页面进行分析后的结果表明事实并不是这样。

第一张图是一个大小为40528bytes的337*191px的大图片的分析结果

第二张图是一个大小为13883bytes的280*90px嘚小图片的分析结果。

一个大小为40528bytes的337*191px的大图片的分析结果(点击图片可以查看完整大图片)

一个大小为13883bytes的280*90px的小图片的分析结果(点击图片鈳以查看完整大图片)

第一张大图片花费时间为:

真正用于传输大文件花费的时间为Reveive时间即4.596s,多数的时间是用来检索缓存和确定链接是否有效的Blocked时间供花费13.034s,占总时间的73.2%

第二张小图片花费时间为:

真正用于传输文件的花费时间是Reveive时间,即0.397s这的确要比刚才大文件的4.596s小佷多。但是他的Blocked时间为16.274s占总时间的97%。

如果这些数据还不够说服你的话让我们看看下面这张图。这里列出了某个网页中所有图片中的花費时间示意图当然,里面的图片有大有小规格不一。


}

我要回帖

更多关于 如何提升组织效率 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信