爱收集资源网

如何让网站更流畅?

网络 2023-06-24 14:08

这篇文章主要关注的是资源加载以后的性能,由于大多数用户关注的不是应用怎么加载而是具体的使用。所以要快速响应用户,尤其是无线端,我们有必要了解浏览器渲染性能。

RAIL性能模型

首先一个须要思索的问题,如何的网站是顺畅的?我们可能可以给一个大约的觉得,如:秒级响应等。虽然,也可以给出一个很应景的答案:用户认为顺畅的网站它就是顺畅的。由于几乎所有网站都希望将用户留在页面上,其实以用户为中心构建性能模型是必要的。下边是Google提出的一个以用户为中心的性能模型,上面的数据不是Google首创,有一些论文做类似研究(如:100ms响应用户是一个很合适的时间等)。

上图是RAIL的具体含意,这儿有一些关键性的数据指标:

应用要达到前面的性能模型须要从什么方面入手呢?假如我们晓得浏览器是怎样渲染一个页面的,而且去优化渲染过程中的关键步骤,是不是能够事半功倍呢?

关键渲染路径

上图是浏览器渲染的关键路径,首先,让我们从浏览器解析一个页面开始吧。

假如我们是做一个动漫,通常会用JS修改相应款式,接着浏览器都会经历JS运行、样式估算、布局、绘制、合成等多个重要步骤(前面都会提到这个步骤实际过程中可以更长或则更短)。这么要做的优化就是在这几个步骤中进行优化而且尽量除去中间的历时步骤。

优化JavaScript的执行

改变瞳孔大小_浏览器大小改变事件_pscs5如何改变图层大小

上图描述的四个场景都是有可能对响应用户输入或则动漫导致影响的。函数的输入风波处理、不合时机的JS、长时间的JS运行以及垃圾回收。

函数的输入风波处理

首先,我们要晓得的一个事实就是浏览器是由多个处理进程的:Compositor、TileWorker、Main。当用户进行输入操作(滚动、点击等),如滚动时,Compositor进程会接收到这个风波(实际它可以接受任何用户输入风波),倘若可以的话,它将不会通知主进程,直接说:滚吧,牛小孩。于是,页面就滚动了。其实,这其中包含更新层定位以及让GPU勾画帧,而主线程处于空闲状态。并且,事情常常并非这么。倘若输入风波上绑定了JS处理风波的话,Compositor进程就没办法主动跳过主进程了。

如上图,当JS处理风波过长时,输入风波的响应会始终处于阻塞状态,直至JS处理完成。当响应超过100ms时,用户都会感遭到延时。所以当处理用户风波时,我们应当做到:

优化处理

其他优化:

款式估算

添加或移除一个DOM元素、修改元素属性和款式类、应用动漫疗效等操作,就会导致DOM结构的改变,进而造成浏览器须要重新估算每位元素的式样、对页面或其二部份重新布局(多数情况下)。

浏览器大小改变事件_pscs5如何改变图层大小_改变瞳孔大小

估算款式的第一步是创建一套匹配的款式选择器,浏览器就是靠它们来对一个元素应用款式的。第二步是按照匹配的款式选择器来获取对应的具体式样规则,估算出最终具体有什么款式是要应用在DOM元素上的。所以款式的优化也是这两步:

减少选择器的复杂性

怎么减少选择器的复杂性?

.box:nth-last-child(-n+1) .title {
  /* styles */
}
.final-box-title {
  /* styles */
}

里面代码都是选择同一个元素,当元素好多时,第二个选择器的性能会显著优于第一个。BEM规范有做类似事情,根据特点直接由一个选择器选择元素的性能常常会更优。

降低款式的估算量

由于元素的估算量和被改变的元素的数目成反比,所以你只须要注意一点,降低无效元素。

 
   

多层无意义的标签

 

像前面的反例,有时侯创建了一些冗余的标签。当改变内层的款式时,冗余的标签也须要进行款式估算,浪费性能。

布局

浏览器估算DOM元素的几何信息的过程:元素大小和在页面中的位置。每位元素都有一个显式或隐式的大小信息,决定于其CSS属性的设置、或是元素本身内容的大小、或者是其父元素的大小。在Blink/WebKit内核的浏览器和IE中,这个过程称为Layout。在基于Gecko的浏览器(例如Firefox)中,这个过程称为Reflow。

浏览器大小改变事件_pscs5如何改变图层大小_改变瞳孔大小

防止触发布局

目前,transform和opacity只会造成合成,不会造成布局和重新勾画。整个流程中比较花费性能的布局和勾画流程将直接跳过,性能其实是挺好的。其他的CSS属性改变导致的流程会有所不同,有些属性也会跳过布局,具体可以查看CSSTriggers。所以,优化的第一步就是尽可能避开触发布局。

使用Flexbox布局

Flexbox布局方案性能会优于先前的布局方案,并且目前浏览器对Flexbox支持度相当高了:

防止强制同步布局风波

首先是执行JS脚本,之后是款式估算,之后是布局。并且,我们还可以强制浏览器在执行JS脚本之前先执行布局过程,这就是所谓的强制同步布局。在JS脚本运行的时侯,它能获取到的元素款式属性值都是上一帧画面的,都是旧的值。因而,假如你想在这一帧开始的时侯,读取一个元素的height属性,你可以会写出这样的JS代码:

function logBoxHeight() {
  box.classList.add('super-big');
  // Gets the height of the box in pixels
  // and logs it out.
  console.log(box.offsetHeight);
}

为了给你返回box的height属性值,浏览器必须首先应用box的属性更改(由于对其添加了super-big款式),接着执行布局过程。在这以后,浏览器能够返回正确的height属性值。这样就导致了同步布局风波,是十分消耗性能的。大多数情况下,你应当都不须要先更改之后再读取元素的款式属性值,使用上一帧的值就足够了。过早地同步执行款式估算和布局是潜在的页面性能的困局之一。

function logBoxHeight() {
  // Gets the height of the box in pixels
  // and logs it out.
  console.log(box.offsetHeight);
  box.classList.add('super-big');
}

防止快速连续的布局

还有一种情况比强制同步布局更糟:连续快速的多次执行它。

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';
  }
}

上述代码对一组段落标签执行循环操作,设置p标签的width属性值,使其与box元素的长度相同。看起来这段代码是没问题的,但问题在于,在每次循环中,都读取了box元素的一个款式属性值,之后立刻使用该值来更新p元素的width属性。在下一次循环中读取box元素offsetwidth属性的时侯,浏览器必须先促使上一次循环中的款式更新操作生效,也就是执行布局过程,之后才会响应本次循环中的款式读取操作。布局过程将在每次循环中发生。优化代码:

// Read.
var width = box.offsetWidth;
function resizeAllParagraphsToMatchBlockWidth() {
  for (var i = 0; i < paragraphs.length; i++) {
    // Now write.
    paragraphs[i].style.width = width + 'px';
  }
}

假如你想确保编撰的读写操作是安全的,你可以使用FastDOM。它能帮你手动完成读写操作的批处理,能够防止意外地触发强制同步布局或快速连续的布局。

勾画

提高联通或渐变元素的勾画层

勾画并非总是在显存中的双层画面里完成的。实际上,浏览器在必要时将会把一帧画面勾画成多层画面,之后将这若干层画面合并成一张图片显示到屏幕上。通过渲染层提高可以减少勾画区域,我们可以用调试工具查看到勾画层:

在页面中新建一个渲染层最好的方法就是使用will-change属性,同时再与transform属性一起使用,都会创建一个新的组合层:

.element {
  will-change: transform;
}

浏览器大小改变事件_改变瞳孔大小_pscs5如何改变图层大小

对于这些目前还不支持will-change属性、但支持创建渲染层的浏览器,可以使用一个3Dtransform属性来强制浏览器创建一个新的渲染层:

.element {
  transform: translateZ(0);
}

注意:别盲目创建渲染层,一定要剖析其实际性能表现。由于创建渲染层是有代价的,每创建一个新的渲染层,就意味着新的显存分配和更复杂的层的管理。而且在联通端GPU和CPU的带宽有限制,创建的渲染层过多时,合成也会消耗跟多的时间。

仔细规划动漫和简化勾画的复杂度

有时侯,虽然把元素提高到了一个单独的渲染层,浏览器会把两个相邻区域的渲染任务合并在一起进行,这将造成整个屏幕区域就会被勾画。所以可以使用调试工具查看,仔细规划动漫。

不同的CSS属性勾画的成本是不一样的,勾画一个阴影就比勾画边框更费时。其实,这个浏览器也在不停优化中,如今的历时渲染属性随时都可能被改变,所以须要多关注一下。

合成

渲染层的合并,就是把页面中完成了勾画过程的部份合并成一层,之后显示在屏幕上。下边和合成相关的两点上面也有提及过。

使用transform/opacity实现动漫疗效

后面早已提及过transform/opacity的优势,应用了transforms/opacity属性的元素必须独占一个渲染层。为了对这个元素创建一个自有的渲染层,你必须提高该元素。

管理渲染层、避免过多数目的层

创建一个新的渲染层须要消耗额外的显存和管理资源。而在显存资源有限的设备上,因为过多的渲染层来带的开支而对页面渲染性能形成的影响,甚至远远超过了它在性能改善上带来的益处。因为每位渲染层的纹理都须要上传到GPU处理,因而我们还须要考虑CPU和GPU之间的带宽问题、以及有多大显存供GPU处理这种纹理的问题。

其他

浏览器大小改变事件