回流与重绘以及css4优化方法:contain、content-visibility、will-change、font-display

301 阅读10分钟

render tree : dom tree + 层叠样式表

回流

当render tree中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建。这就称为回流(reflow)。每个页面至少需要一次回流

    行为
  • 页面首次渲染
  • 浏览器窗口大小发生改变
  • 元素尺寸或者位置发生改变
  • 元素内容变化(文字数量或者图片大小发生改变)
  • 元素字体大小的改变
  • 添加或者删除可见的 DOM 元素
  • 激活 CSS 伪类 (eg: :hover)
  • 查询某些属性或调用某些方法

重绘

当render tree中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的,比如background-color。则就叫称为重绘。

    优化方法
  • 将需要多次重排的元素,position属性设为absolute或fixed,这样此元素就脱离了文档流,它的变化不会影响到其他元素。例如有动画效果的元素就最好设置为绝对定位;
  • 使用cssText const el = document.getElementById('test'); el.style.cssText += 'border-left: 1px; border-right: 2px; padding: 5px;'; 修改CSS的class const el = document.getElementById('test'); el.className += ' active';

display or visibility

display 影响文档流,visibility 不影响; display 改变的是 DOM 的显示形式,即以何种方式来计算它的大小;visibility 只是控制可见性,DOM 的大小不会改变。

display: none:隐藏元素并破坏其渲染状态。这意味着取消隐藏元素与渲染具有相同内容的新元素一样昂贵

visibility: hidden:隐藏元素并保持其渲染状态。这并不能真正从文档中删除该元素,因为它(及其子树)仍占据页面上的几何空间,并且仍然可以单击。它也可以在需要时随时更新渲染状态,即使隐藏也是如此

仅使用CSS提高页面渲染速度(渲染优化)

    控制页面的重绘与重排(contain、content-visibility)

  • content-visibility来跳过屏幕外的内容渲染。也就是说,如果你有大量的离屏内容(Off-screen Content),这将会大幅减少页面渲染时间。它有三个属性(visible、auto、hidden)。使用content-visibility: auto属性可使分块的内容区域的初始加载性能提高大约7倍,所有卡片加入content-visibility样式之后,页面的渲染时间下降到150ms,差不多提高了六倍的渲染性能
  • contain-intrinsic-size:它将像有一个“固有尺寸”(Intrinsic-size)的单一子元素一样布局,确保你没设置尺寸的div(示例中的.card)仍然占据空间。contain-intrinsic-size作为一个占位符尺寸来替代渲染内容。用于当前区域不再被渲染时的占位大小声明
  • contain: 该属性允许我们指定特定的DOM元素和它的子元素,让它们能够独立于整个DOM树结构之外。目的是能够让浏览器有能力只对部分元素进行重绘、重排,而不必每次针对整个页面
    • layout :该值表示元素的内部布局不受外部的任何影响,同时该元素以及其内容也不会影响以上级(告诉浏览器,容器的后代不应该导致其容器外元素的布局改变,反之亦然)
    • paint :该值表示元素的子级不能在该元素的范围外显示,该元素不会有任何内容溢出(或者即使溢出了,也不会被显示)告诉浏览器,容器的内容将永远不会绘制超出容器的尺寸,如果容器是模糊的,那么就根本不会绘制内容
    • size :该值表示元素盒子的大小是独立于其内容,也就是说在计算该元素盒子大小的时候是会忽略其子元素(告诉浏览器,当其内容发生变化时,该容器不应导致页面上的位置移动)
    • content :该值是contain: layout paint的简写
    • strict :该值是contain: layout paint size的简写
  • 预先优化(will-change)

  • 使用CSS的will-change属性,该属性可以表明元素将修改特定的属性,让浏览器事先进行必要的优化。也就是说,will-change是一个UA提示,它不会对你使用它的元素产生任何样式上的影响。但值得注意的是,如果创建了新的层叠上下文,它可以产生外观效果。
  • 动画优画: 如果你试图将will-change和动画同时使用,它将不会给你带来优化。因此,建议在父元素上使用will-change,在子元素上使用动画。
  • 不要将 will-change 应用到太多元素上:浏览器已经尽力尝试去优化一切可以优化的东西了。有一些更强力的优化,如果与 will-change 结合在一起的话,有可能会消耗很多机器资源,如果过度使用的话,可能导致页面响应缓慢或者消耗非常多的资源。比如 *{will-change: transform, opacity;}
  • 有节制地使用:通常,当元素恢复到初始状态时,浏览器会丢弃掉之前做的优化工作。但是如果直接在样式表中显式声明了 will-change 属性,则表示目标元素可能会经常变化,浏览器会将优化工作保存得比之前更久。所以最佳实践是当元素变化之前和之后通过脚本来切换 will-change 的值
  • 不要过早应用 will-change 优化:如果你的页面在性能方面没什么问题,则不要添加 will-change 属性来榨取一丁点的速度。will-change 的设计初衷是作为最后的优化手段,用来尝试解决现有的性能问题。它不应该被用来预防性能问题。过度使用 will-change 会导致大量的内存占用,并会导致更复杂的渲染过程,因为浏览器会试图准备可能存在的变化过程。这会导致更严重的性能问题。
  • 建议在完成所有动画后,将元素的will-change删除。下面这个示例展示如何使用脚本正确地应用 will-change 属性的示例,在大部分的场景中,你都应该这样做。
    
    // 当鼠标移动到该元素上时给该元素设置 will-change 属性
    el.addEventListener('mouseenter', hintBrowser);
    // 当 CSS 动画结束后清除 will-change 属性
    el.addEventListener('animationEnd', removeHint);
    
    function hintBrowser() {
        // 填写上那些你知道的,会在 CSS 动画中发生改变的 CSS 属性名们
        this.style.willChange = 'transform, opacity';
    }
    
    function removeHint() {
        this.style.willChange = 'auto';
    }
    
  • 在样式表中少用will-change 给will-change足够的时间令其发挥该有的作用 使用来针对超特定的变化(如,left, opacity等) 如果需要的话,可以JavaScript中使用它(添加和删除) 修改完成后,删除will-change 不要同时声明太多的属性 不要应用在太多元素上 不要把资源浪费在已停止变化的元素上
  • 使用font-display解决由于字体造成的布局偏移

  • auto:auto:默认值。典型的浏览器字体加载的行为会发生,也就是使用自定义字体的文本会先被隐藏,直到字体加载结束才会显示。即字体展示策略与浏览器一致,当前,大多数浏览器的默认策略类似block
  • block: 给予字体一个较短的阻塞时间(大多数情况下推荐使用 3s)和无限大的交换时间。换言之,如果字体未加载完成,浏览器将首先绘制“隐形”文本;一旦字体加载完成,立即切换字体。为此,浏览器将创建一个匿名字体,其类型与所选字体相似,但所有字形都不含“墨水”。使用特定字体渲染文本之后页面方才可用,只有这种情况下才应该使用 block。
  • swap: 使用 swap,则阻塞阶段时间为 0,交换阶段时间无限大。也就是说,如果字体没有完成加载,浏览器会立即绘制文字,一旦字体加载成功,立即切换字体。与 block 类似,如果使用特定字体渲染文本对页面很重要,且使用其他字体渲染仍将显示正确的信息,才应使用 swap。
  • fallback: 这个可以说是auto和swap的一种折中方式。需要使用自定义字体渲染的文本会在较短的时间不可见,如果自定义字体还没有加载结束,那么就先加载无样式的文本。一旦自定义字体加载结束,那么文本就会被正确赋予样式。使用 fallback时,阻塞阶段时间将非常小(多数情况下推荐小于 100ms),交换阶段也比较短(多数情况下建议使用 3s)。换言之,如果字体没有加载,则首先会使用后备字体渲染。一旦加载成功,就会切换字体。但如果等待时间过久,则页面将一直使用后备字体。如果希望用户尽快开始阅读,而且不因新字体的载入导致文本样式发生变动而干扰用户体验,fallback 是一个很好的选择。
  • optional:效果和fallback几乎一样,都是先在极短的时间内文本不可见,然后再加载无样式的文本。不过optional选项可以让浏览器自由决定是否使用自定义字体,而这个决定很大程度上取决于浏览器的连接速度。如果速度很慢,那你的自定义字体可能就不会被使用。使用 optional 时,阻塞阶段时间会非常小(多数情况下建议低于 100ms),交换阶段时间为 0。

其它优化方式

    让滚动更流畅

  • scroll-behavior是CSSOM View Module提供的一个新特性,可以轻易的帮助我们实现丝滑般的滚动效果。该属性可以为一个滚动框指定滚动行为,其他任何的滚动,例如那些由于用户行为而产生的滚动,不受这个属性的影响。
    • auto: 滚动框立即滚动
    • smooth: 滚动框通过一个用户代理定义的时间段使用定义的时间函数来实现平稳的滚动,用户代理平台应遵循约定,如果有的话
  • overscroll-behavior
  • 开启GPU渲染动画:浏览器针对处理CSS动画和不会很好地触发重排(因此也导致绘)的动画属性进行了优化。为了提高性能,可以将被动画化的节点从主线程移到GPU上。将导致合成的属性包括 3D transforms (transform: translateZ(), rotate3d(),等),animating, transform 和 opacity, position: fixed,will-change,和 filter
  • 减少渲染阻止时间: 浏览器假设每个指定的样式表都是阻塞渲染的。通过添加 media属性附加媒体查询,告诉浏览器何时应用样式表。当浏览器看到一个它知道只会用于特定场景的样式表时,它仍会下载样式,但不会阻塞渲染。通过将 CSS 分成多个文件,主要的 阻塞渲染 文件(本例中为 styles.css)的大小变得更小,从而减少了渲染被阻塞的时间。
    与使用 @import 相比,我们可以通过多个 link 来实现同样的功能,但性能要好得多,因为它允许我们并行加载样式表。
附上参考链接:https://blog.logrocket.com/5-new-css-features-you-can-already-test/#3thecontentvisibilityproperty

回流和重绘优化手段.png