热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

精读《webreflow》

网页重排(回流)是阻碍流畅性的重要原因之一,结合Whatforceslayoutreflow这篇文章与引用,整理一下回流的起

网页重排(回流)是阻碍流畅性的重要原因之一,结合 What forces layout / reflow 这篇文章与引用,整理一下回流的起因与优化思考。

借用这张经典图:

db185cf3c3dbfb6045f5a64912986a72.png

网页渲染会经历 DOM -> CSSOM -> Layout(重排 or reflow) -> Paint(重绘) -> Composite(合成),其中 Composite 在 精读《深入了解现代浏览器四》 详细介绍过,是在 GPU 进行光栅化。

那么排除 JS、DOM、CSSOM、Composite 可能导致的性能问题外,剩下的就是我们这次关注的重点,reflow 了。从顺序上可以看出来,重排后一定重绘,而重绘不一定触发重排。

概述

什么时候会触发 Layout(reflow) 呢?一般来说,当元素位置发生变化时就会。但也不尽然,因为浏览器会自动合并更改,在达到某个数量或时间后,会合并为一次 reflow,而 reflow 是渲染页面的重要一步,打开浏览器就一定会至少 reflow 一次,所以我们不可能避免 reflow。

那为什么要注意 reflow 导致的性能问题呢?这是因为某些代码可能导致浏览器优化失效,即明明能合并 reflow 时没有合并,这一般出现在我们用 js API 访问某个元素尺寸时,为了保证拿到的是精确值,不得不提前触发一次 reflow,即便写在 for 循环里。

当然也不是每次访问元素位置都会触发 reflow,在浏览器触发 reflow 后,所有已有元素位置都会记录快照,只要不再触发位置等变化,第二次开始访问位置就不会触发 reflow,关于这一点会在后面详细展开。现在要解释的是,这个 ”触发位置等变化“,到底有哪些?

根据 What forces layout / reflow 文档的总结,一共有这么几类:

获得盒子模型信息

  • elem.offsetLeft, elem.offsetTop, elem.offsetWidth, elem.offsetHeight, elem.offsetParent

  • elem.clientLeft, elem.clientTop, elem.clientWidth, elem.clientHeight

  • elem.getClientRects(), elem.getBoundingClientRect()

获取元素位置、宽高的一些手段都会导致 reflow,不存在绕过一说,因为只要获取这些信息,都必须 reflow 才能给出准确的值。

滚动

  • elem.scrollBy(), elem.scrollTo()

  • elem.scrollIntoView(), elem.scrollIntoViewIfNeeded()

  • elem.scrollWidth, elem.scrollHeight

  • elem.scrollLeft, elem.scrollTop 访问及赋值

scrollLeft 赋值等价于触发 scrollTo,所有导致滚动产生的行为都会触发 reflow,笔者查了一些资料,目前主要推测是滚动条出现会导致可视区域变窄,所以需要 reflow。

focus()

  • elem.focus() (源码)

可以根据源码看一下注释,主要是这一段:

// Ensure we have clean style (including forced display locks).
GetDocument().UpdateStyleAndLayoutTreeForNode(this)

即在聚焦元素时,虽然没有拿元素位置信息的诉求,但指不定要被聚焦的元素被隐藏或者移除了,此时必须调用 UpdateStyleAndLayoutTreeForNode 重排重绘函数,确保元素状态更新后才能继续操作。

还有一些其他 element API:

  • elem.computedRole, elem.computedName

  • elem.innerText (源码)

innerText 也需要重排后才能拿到正确内容。

获取 window 信息

  • window.scrollX, window.scrollY

  • window.innerHeight, window.innerWidth

  • window.visualViewport.height / width / offsetTop / offsetLeft (源码)

和元素级别一样,为了拿到正确宽高和位置信息,必须重排。

document 相关

  • document.scrollingElement 仅重绘

  • document.elementFromPoint

elementFromPoint 因为要拿到精确位置的元素,必须重排。

Form 相关

  • inputElem.focus()

  • inputElem.select(), textareaElem.select()

focusselect 触发重排的原因和 elem.focus 类似。

鼠标事件相关

  • mouseEvt.layerX, mouseEvt.layerY, mouseEvt.offsetX, mouseEvt.offsetY (源码)

鼠标相关位置计算,必须依赖一个正确的排布,所以必须触发 reflow。

getComputedStyle

getComputedStyle 通常会导致重排和重绘,是否触发重排取决于是否访问了位置相关的 key 等因素。

Range 相关

  • range.getClientRects(), range.getBoundingClientRect()

获取选中区域的大小,必须 reflow 才能保障精确性。

SVG

大量 SVG 方法会引发重排,就不一一枚举了,总之使用 SVG 操作时也要像操作 dom 一样谨慎。

contenteditable

被设置为 contenteditable 的元素内,包括将图像复制到剪贴板在内,大量操作都会导致重排。(源码)

精读

What forces layout / reflow 下面引用了几篇关于 reflow 的相关文章,笔者挑几个重要的总结一下。

repaint-reflow-restyle

repaint-reflow-restyle 提到现代浏览器会将多次 dom 操作合并,但像 IE 等其他内核浏览器就不保证有这样的实现了,因此给出了一个安全写法:

// bad
var left = 10,top = 10;
el.style.left = left + "px";
el.style.top  = top  + "px";// better 
el.className += " theclassname";// or when top and left are calculated dynamically...// better
el.style.cssText += "; left: " + left + "px; top: " + top + "px;";

比如用一次 className 的修改,或一次 cssText 的修改保证浏览器一定触发一次重排。但这样可维护性会降低很多,不太推荐。

avoid large complex layouts

avoid large complex layouts 重点强调了读写分离,首先看下面的 bad case:

function resizeAllParagraphsToMatchBlockWidth() {// Puts the browser into a read-write-read-write cycle.for (var i &#61; 0; i < paragraphs.length; i&#43;&#43;) {paragraphs[i].style.width &#61; box.offsetWidth &#43; &#39;px&#39;;}
}

在 for 循环中不断访问元素宽度&#xff0c;并修改其宽度&#xff0c;会导致浏览器执行 N 次 reflow。

虽然当 Javascript 运行时&#xff0c;前一帧中的所有旧布局值都是已知的&#xff0c;但当你对布局做了修改后&#xff0c;前一帧所有布局值缓存都会作废&#xff0c;因此当下次获取值时&#xff0c;不得不重新触发一次 reflow。

而读写分离的话&#xff0c;就代表了集中读&#xff0c;虽然读的次数还是那么多&#xff0c;但从第二次开始就可以从布局缓存中拿数据&#xff0c;不用触发 reflow 了。

另外还提到 flex 布局比传统 float 重排速度快很多&#xff08;3ms vs 16ms&#xff09;&#xff0c;所以能用 flex 做的布局就尽量不要用 float 做。

really fixing layout thrashing

really fixing layout thrashing 提到了用 fastdom 实践读写分离&#xff1a;

ids.forEach(id &#61;> {fastdom.measure(() &#61;> {const top &#61; elements[id].offsetTopfastdom.mutate(() &#61;> {elements[id].setLeft(top)})})
})

fastdom 是一个可以在不分离代码的情况下&#xff0c;分离读写执行的库&#xff0c;尤其适合用在 reflow 性能优化场景。每一个 measuremutate 都会推入执行队列&#xff0c;并在 window.requestAnimationFrame 时机执行。

总结

回流无法避免&#xff0c;但需要控制在正常频率范围内。

我们需要学习访问哪些属性或方法会导致回流&#xff0c;能不使用就不要用&#xff0c;尽量做到读写分离。在定义要频繁触发回流的元素时&#xff0c;尽量使其脱离文档流&#xff0c;减少回流产生的影响。

讨论地址是&#xff1a;精读《web reflow》· Issue #420 · dt-fe/weekly

如果你想参与讨论&#xff0c;请 点击这里&#xff0c;每周都有新的主题&#xff0c;周末或周一发布。前端精读 - 帮你筛选靠谱的内容。

版权声明&#xff1a;自由转载-非商用-非衍生-保持署名&#xff08;创意共享 3.0 许可证&#xff09;



推荐阅读
  • 利用Visual Basic开发SAP接口程序初探的方法与原理
    本文介绍了利用Visual Basic开发SAP接口程序的方法与原理,以及SAP R/3系统的特点和二次开发平台ABAP的使用。通过程序接口自动读取SAP R/3的数据表或视图,在外部进行处理和利用水晶报表等工具生成符合中国人习惯的报表样式。具体介绍了RFC调用的原理和模型,并强调本文主要不讨论SAP R/3函数的开发,而是针对使用SAP的公司的非ABAP开发人员提供了初步的接口程序开发指导。 ... [详细]
  • 本文介绍了停用Vaadin焦点颜色的全局方法。焦点环是一种辅助功能,用于指示字段已从键盘交互获得焦点。每个组件和主题的焦点环样式不同。文章提供了一种方便的方法来找到和修改焦点环样式,通过检查shadow DOM中的标签并覆盖相应的样式。同时,还介绍了使用with或导入样式表的方法来应用修改后的样式。 ... [详细]
  • 第8章 使用外部和内部链接
    8.1使用web地址LearnAboutafricanelephants. ... [详细]
  • angular.element使用方法及总结
    2019独角兽企业重金招聘Python工程师标准在线查询:http:each.sinaapp.comangularapielement.html使用方法 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 展开全部下面的代码是创建一个立方体Thisexamplescreatesanddisplaysasimplebox.#Thefirstlineloadstheinit_disp ... [详细]
  • Html5-Canvas实现简易的抽奖转盘效果
    本文介绍了如何使用Html5和Canvas标签来实现简易的抽奖转盘效果,同时使用了jQueryRotate.js旋转插件。文章中给出了主要的html和css代码,并展示了实现的基本效果。 ... [详细]
  • position属性absolute与relative的区别和用法详解
    本文详细解读了CSS中的position属性absolute和relative的区别和用法。通过解释绝对定位和相对定位的含义,以及配合TOP、RIGHT、BOTTOM、LEFT进行定位的方式,说明了它们的特性和能够实现的效果。同时指出了在网页居中时使用Absolute可能会出错的原因,即以浏览器左上角为原始点进行定位,不会随着分辨率的变化而变化位置。最后总结了一些使用这两个属性的技巧。 ... [详细]
  • 本文介绍了前端人员必须知道的三个问题,即前端都做哪些事、前端都需要哪些技术,以及前端的发展阶段。初级阶段包括HTML、CSS、JavaScript和jQuery的基础知识。进阶阶段涵盖了面向对象编程、响应式设计、Ajax、HTML5等新兴技术。高级阶段包括架构基础、模块化开发、预编译和前沿规范等内容。此外,还介绍了一些后端服务,如Node.js。 ... [详细]
  • 本文介绍了绕过WAF的XSS检测机制的方法,包括确定payload结构、测试和混淆。同时提出了一种构建XSS payload的方法,该payload与安全机制使用的正则表达式不匹配。通过清理用户输入、转义输出、使用文档对象模型(DOM)接收器和源、实施适当的跨域资源共享(CORS)策略和其他安全策略,可以有效阻止XSS漏洞。但是,WAF或自定义过滤器仍然被广泛使用来增加安全性。本文的方法可以绕过这种安全机制,构建与正则表达式不匹配的XSS payload。 ... [详细]
  • JavaScript和HTML之间的交互是经由过程事宜完成的。事宜:文档或浏览器窗口中发作的一些特定的交互霎时。能够运用侦听器(或处置惩罚递次来预订事宜),以便事宜发作时实行相应的 ... [详细]
  • Android实战——jsoup实现网络爬虫,糗事百科项目的起步
    本文介绍了Android实战中使用jsoup实现网络爬虫的方法,以糗事百科项目为例。对于初学者来说,数据源的缺乏是做项目的最大烦恼之一。本文讲述了如何使用网络爬虫获取数据,并以糗事百科作为练手项目。同时,提到了使用jsoup需要结合前端基础知识,以及如果学过JS的话可以更轻松地使用该框架。 ... [详细]
  • 本文介绍了Python语言程序设计中文件和数据格式化的操作,包括使用np.savetext保存文本文件,对文本文件和二进制文件进行统一的操作步骤,以及使用Numpy模块进行数据可视化编程的指南。同时还提供了一些关于Python的测试题。 ... [详细]
  • 本文介绍了2015年九月八日的js学习总结及相关知识点,包括参考书《javaScript Dom编程的艺术》、js简史、Dom、DHTML、解释型程序设计和编译型程序设计等内容。同时还提到了最佳实践是将标签放到HTML文档的最后,并且对语句和注释的使用进行了说明。 ... [详细]
  • 本文总结了在编写JS代码时,不同浏览器间的兼容性差异,并提供了相应的解决方法。其中包括阻止默认事件的代码示例和猎取兄弟节点的函数。这些方法可以帮助开发者在不同浏览器上实现一致的功能。 ... [详细]
author-avatar
邹balitas_611
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有