如今动辄几兆大小的页面加载量,让性能优化成了不可避免的热门话题。WEB 应用越流畅,用户体验就会越好,继而带来更多的访问量。这也就是说,咱们应该检讨一下那些过分美化的 CSS3 动画和多重操做的 DOM 元素是否都考虑到了在性能方面的影响。在说性能优化以前,咱们有必要理清浏览器视觉绘制方面的两个术语:css
Repaint(重绘):若是某些操做影响了 DOM 元素的可见性,但又没有影响布局,那么就会发生浏览器的重绘,好比opacity,background-color,visibility 和 outline 属性。因为浏览器必须检查 DOM 中全部节点的可见性——某些图层或许会置于重绘元素的图层下面,因此重绘是一个很是繁重的逻辑。gulp
Reflow(回流):回流是一个更具破坏性的操做,它会让浏览器从新计算全部元素的坐标位置和尺寸大小。每每因为一个元素的变化,继而引发其子元素、父元素以及相邻元素的变化。浏览器
无论用户或者应用自己是否正在执行某些逻辑,这两种操做都会阻塞浏览器进程。极端状况下,一个 CSS 效果会下降 JavaScript 的执行速度。下面是触发回流事件的几种情境:性能优化
添加、删除和修改可见的 DOM 元素 添加、删除和修改部分 CSS 样式,好比修改元素的宽度,会影响其相邻元素的布局位置 CSS3 动画和过渡效果 使用 offsetWidth 和 offsetHeight。这种情境很诡异,读取一个元素的 offsetWidth 和 offsetHeight 属性会触发回流 用户行为,好比鼠标悬停、输入文本、调整窗口大小、修改字体样式等等 浏览器的底层实现各有不一样,因此渲染页面的开销也各有轻重。好在咱们有一些一般规则能够进行性能优化。app
虽然已是 2015 了,但我仍是要说不要使用行内联样式和 table 布局。框架
HTML 文档下载完成后,行内样式会触发一次额外的回流事件。解析器在解析 table 布局时须要计算大量的单元格的尺寸,因此是件很重的操做。因为单元格每每是根据表头宽度肯定的,因此使用 table-layout: fixed 能够缓解部分性能消耗。grunt
使用 Flexbox 布局也存在性能损失,由于在页面加载完成后,flex item 可能会发生位置和尺寸的变化。工具
样式越少,回流越快,此外,尽可能不要使用过于复杂的选择器。这一问题尤为突出在使用相似 Bootstrap 框架的网站上。使用Unused CSS,uCSS,grunt-uncss 和 gulp-uncss 等工具能够有效剔除无用样式。布局
精简 DOM 层级,指的是减小 DOM 树的级数已经每一分支上 DOM 元素的数量,结果就是层级越少、数量越少,回流越快。此外,若是无需考虑旧版本浏览器,应该尽可能剔除无心义的包裹类标签和层级。性能
操做 DOM 树时的粒度要尽量细化,这有助于减弱局部 DOM 变化给总体带来的影响。
应该确保使用动画的元素脱离了文档流,使用 position: absolute 和 position: fixed 属性脱离文档流的元素会被浏览器建立一个新层来存放,这些图层上的修改不会影响其余图层上的元素。
使用 display: none; 隐藏的元素不会触发页面的重绘和回流事件,因此能够在这些元素隐藏期间配置样式,配置完成后再转换为可见状态。
单词更新全部 DOM 元素的性能要优于屡次更新。下面这段代码触发了三次页面回流:
var myelement = document.getElementById('myelement'); myelement.width = '100px'; myelement.height = '200px'; myelement.style.margin = '10px';
经过如下代码能够精简为一次页面回流事件,而且提升了代码的可维护性:
var myelement = document.getElementById('myelement'); myelement.classList.add('newstyles'); .newstyles { width: 100px; height: 200px; margin: 10px; }
同理,咱们还能够减小操做 DOM 的频率。假设咱们要建立一个以下所示的无序列表:
若是分次添加每个 item 将会触发屡次页面回流,简单而高效的方式是使用 DOM fargment 在内存中建立完整的 DOM 节点,而后一次性添加到 DOM 中:
var i, li, frag = document.createDocumentFragment(), ul = frag.appendChild(document.createElement('ul')); for (i = 1; i <= 3; i++) { li = ul.appendChild(document.createElement('li')); li.textContent = 'item ' + i; } document.body.appendChild(frag);
这里的约束是指,尽可能避免某个元素的变化引发大范围的变化。假设咱们有一个 tab 选项卡的组件,选项卡内部的内容长短不一,这就致使了每一个选项卡的高度不惟一。这一设计带来的问题就是每次切换选项卡时,周围的元素都要从新布局。咱们能够经过一个固定高度来避免这一状况。
一次移动一像素的位置看起来虽然很流畅,但对于某些低性能终端会是很大的压力。一次移动四像素下降帧速虽然看起来稍有些迟钝,但性能压力下降了。这就是须要咱们权衡的地方:流畅度和性能。
目前主流浏览器都在开发者工具中提供了监控页面重绘的功能。在 Blink/Webkit 内核的浏览器中,使用 Timeline 面板能够记录一个页面活动详情:
下面是火狐开发者工具中的 TimeLine:
在 IE 中这个功能被放置在了 UI Responsiveness 面板中:
全部的浏览器都使用绿色来显示页面重绘和页面回流事件。上面的测试只是几个简单的示例,其中没有调用繁重的动画效果,因此布局渲染在总时间中占据了较大比重。减小页面回流和页面重绘,天然提升页面性能。