前端学习笔记-性能相关知识

评估7指标

在这里插入图片描述

资源解析

在这里插入图片描述

评估模型 RAIL

在这里插入图片描述

  • R响应:立即响应用户;在 100 毫秒以内确认用户输入,但预算只有 50 毫秒
  • A动画:设置动画或滚动时,在 10 毫秒以内生成帧。
  • I空闲:最大程度增加主线程的空闲时间,以增加页面在 50 毫秒内响应用户输入的几率。
  • L加载:持续吸引用户;在 1000 毫秒以内呈现交互内容。

60fps 与设备刷新率 多数设备屏幕刷新率为 60 次/秒。动画或页面帧速率最好刷新率保持一致。 因此每帧的预算时间比 16 毫秒多一点 (1 秒/ 60 = 16.66 毫秒)。但实际上浏览器有整理工作要做,预算应该再 10 毫秒以内完成。

帧 Frame

https://aerotwist.com/blog/the-anatomy-of-a-frame/

Frame的一生实际涉及两个Process,然鹅我们常常只关注到那个Renderer Process中的Main Thread做的渲染部分……
在这里插入图片描述
因为,除GPU以外我们也只能研究一下Renderer Process了:
在这里插入图片描述
浏览器不需要执行主线程中的所有步骤,这取决于实际情况。例如,如果没有要解析的新 HTML,则不会触发 Parse HTML。实际上,提高性能的最好方法 就是消除部分流程!

重计算StylesLayout可能导致流程回到rAF,这种现象被称为强制同步布局(Forced Synchronous Layout),在开发者工具Performance面板中可能会发现写着Layout的紫色块,点击甚至能找到对应的代码,比如:
在这里插入图片描述

  1. 帧开始:Vsync被触发,一个Frame开始了。
  2. Input事件处理程序:输入数据从合成器线程传递过来。所有事件的handler(触摸移动、滚动、点击)都应该首先启动,每帧一次,但情况根据操作系统和延迟情况也不一定如此;
  3. requestAnimationFrame:这是对屏幕进行视觉更新的理想场所,其他的如样式计算的视觉任务,是在这个rAF任务之后进行的,所以此处是突变元素的理想位置。
    比如说如果你突然修改了100个class,这不会导致100个样式计算;它们会被分批,然后再进行处理。唯一需要注意的是,不要查询任何计算样式或布局属性(如el.style.backgroundImage或el.style.offsetWidth)这会把重计算样式、布局或两者一起提前,导致强制同步布局,或者更糟糕的布局抖动(layout thrashing)。详细
  4. 解析HTML:新添加的HTML会被处理并创建DOM元素,发生在页面加载期间或appendChild这样的操作。
  5. 重计算样式:对新添加或改变的东西进行样式计算。这可能是整个树也可能是后代,例如,改变body上的类可能影响较深;
  6. 布局:对每个可见元素的几何信息(位置和大小)进行计算。这通常是针对整个文档进行的,通常使计算成本与DOM大小成正比。
  7. 更新图层树:创建堆叠上下文(stacking contexts)和深度排序元素(depth sorting)的过程。
  8. 绘制(Paint):这是两部分过程中的第一部分:绘制 是对任何新的或在视觉上有变化的元素 进行绘制调用的记录(在这里填一个矩形,在那里写文字)。第二部分其实是栅格化(Rasterization, 见后),在这里执行绘图调用,并填入纹理。
    对绘制调用进行记录 通常比栅格化执行绘制调用 要快得多,但这两部分经常被统称为 “绘制”。
  9. 合成(Composite):计算图层和切片(tile)信息,并将其传回给合成器线程以便处理。这将涉及到像will-change,重叠元素,以及任何硬件加速的canvas。
  10. 栅格调度和栅格化(Raster Scheduled and Rasterize):绘制任务中记录的绘制调用现在被执行。这是在合成器栅格切片Worker中完成的。Worker的数量取决于平台和设备能力,在Android上通常是1个Worker,在桌面上有时会找到4个。栅格化是以Layer为单位进行的,每个Layer都是由切片组成的。
  11. 帧结束:随着各层的切片全部栅格化,任何新的切片都会和数据一起提交给GPU线程。
  12. 帧片:最后,栅格切片由GPU线程上传到GPU。GPU使用quads和matrices(所有常见的GL特性)将切片绘制到屏幕上。

关于Layer 更新图层树

在工作流程中,有两个版本的深度排序会出现:

  1. 堆叠上下文:比如你有两个绝对定位的div重叠。更新图层树是确保z-index和类似的东西被注意到。
  2. 合成器层(Compositor Layers):这是在这个过程的后期,更适用于绘制元素的想法。一个元素可以通过非transform的hack手段,或will-change: transform提升到合成器层,然后可以低开销地在周围进行变换,对动画很有利。但是,如果有重叠的元素,浏览器可能还需要创建额外的合成层来保留由z-index等指定的深度顺序。

这里似乎有另一个版本的Frame Life,补充了另一些细节:
在这里插入图片描述
宏任务-微任务-渲染更新

像素管道 The pixel pipeline

像素至屏幕管道中的关键点:JS / CSS > 样式 > 布局 > 绘制 > 合成
在这里插入图片描述

  • JavaScript。一般来说,我们会使用 JavaScript 来实现一些视觉变化的效果。比如用 jQuery 的 animate 函数做一个动画、对一个数据集进行排序或者往页面里添加一些 DOM 元素等。当然,除了 JavaScript,还有其他一些常用方法也可以实现视觉变化效果,比如: CSS Animations、Transitions 和 Web Animation API。
  • 样式计算。此过程是根据匹配选择器(例如 .headline 或 .nav > .nav__item)计算出哪些元素应用哪些 CSS 规则的过程。从中知道规则之后,将应用规则并计算每个元素的最终样式。
  • 布局。在知道对一个元素应用哪些规则之后,浏览器即可开始计算它要占据的空间大小及其在屏幕的位置。网页的布局模式意味着一个元素可能影响其他元素,例如 元素的宽度一般会影响其子元素的宽度以及树中各处的节点,因此对于浏览器来说,布局过程是经常发生的。
  • 绘制。绘制是填充像素的过程。它涉及绘出文本、颜色、图像、边框和阴影,基本上包括元素的每个可视部分。绘制一般是在多个表面(通常称为层)上完成的。
  • 合成。由于页面的各部分可能被绘制到多层,由此它们需要按正确顺序绘制到屏幕上,以便正确渲染页面。对于与另一元素重叠的元素来说,这点特别重要,因为一个错误可能使一个元素错误地出现在另一个元素的上层。

rAF和rIC

rAF 会保证注册的回调在下次paint页面之前执行,且只会执行一次。另外,当页面处于不可见状态时,rAF 会自动停止执行,以节省系统资源。

优先级

在这里插入图片描述

https://segmentfault.com/a/1190000019154514

也就是说Animation队列优先级小于微任务,而IDLE队列排在渲染之后 优先级顾名思义地最低;

执行特性

并不是每次事件循环都渲染,可能有多次事件循环才出现一次渲染,在一次渲染前会执行rAF,rAF此时算作宏任务;

  • 宏任务,每次只执行一个任务。
  • 微任务,每次执行队列全部任务。如果执行过程中继续往微任务队列中添加任务,新添加的任务也会在当前事件循环中执行;
  • Animation 队列,每次会执行队列全部任务。但如果过程添加新的务,不会在本次循环中执行,而是在下次执行。
  • IDLE队列,每次只会执行一个任务,会根据本帧剩余空闲时间与本任务的长度进行拆分调整,以实际空闲情况可能要等好久才执行。因为调用的频率太低,新版的 React Fiber 不用 rIC,改用 rAF进行模拟了

深入理解:https://juejin.cn/post/6844904165462769678

function onScroll (evt) {
  lastScrollY = window.scrollY;// Store the scroll value for laterz.

  if (scheduledAnimationFrame) // Prevent multiple rAF callbacks.
    return;

  scheduledAnimationFrame = true;
  requestAnimationFrame(readAndUpdatePage);
}

window.addEventListener('scroll', onScroll);

ASYNC 和 DEFER

ASYNC 和 DEFER 的相似之处在于它们允许加载脚本而不阻塞 HTML 解析器,这意味着用户可以更快地看到页面内容。但它们确实有区别:

  • 使用 ASYNC 加载的脚本会在资源下载完成后立即被解析和执行。而 DEFER 脚本在 HTML 文档解析完成(AKA、DOM Interactive 或performance.timing.domInteractive)之前不会执行。
  • ASYNC 脚本可能会乱序加载,而 DEFER 脚本按它们在标记中出现的顺序执行。

尽管 ASYNC 和 DEFER 不会阻止 HTML 解析器,但它们还是会阻止渲染。当它们在渲染完成并接管浏览器主线程之前被解析和执行时,就会发生这种情况。
ASYNC 脚本在下载完成后立即执行,而 DEFER 脚本在 DOM Interactive 之后执行。

懒加载 Lazy Loading

延迟加载是一种在页面加载时 延迟加载非关键资源的技术,这些非关键资源在需要时加载。就图像而言,“非关键”通常与“屏幕外”同义。

这是一种缩短 关键渲染路径长度 的方法,可以缩短页面加载时间。通常会在某些用户交互(例如滚动和导航)上发生。

策略

一般

JS

CSS

在CSSOM 被构造完成之前,浏览器不会渲染任何已处理的内容:绘制 (paint) 仅在布局 (layout) 之后进行,布局 (layout) 则在创建渲染树 (render tree) 之后进行,创建渲染树则需要在DOM和CSSOM 树解析完成后进行。
为了优化CSSOM的构造,请删除不必要的样式,对 CSS 进行最小化,压缩和缓存,并将页面加载时不需要的CSS拆分为其他文件,以减少CSS渲染阻塞。

阻塞渲染优化

媒体查询可以帮助我们优化关键渲染路径。

浏览器会阻塞渲染,直到它解析完全部的样式,但不会阻塞渲染它认为不会使用的样式,例如打印样式表。

通过媒体查询将CSS分成多个文件,可以防止在下载未使用的CSS期间阻止渲染。为了创建非阻塞的 CSS link,我们可以将不立即使用的样式(例如打印样式)移动到单独的文件中,将 <link> 添加到 HTML 中,并添加媒体查询,在这种情况下声明它是打印样式表。

当浏览器看到一个它知道只会用于特定场景的样式表时,它仍会下载样式,但不会阻塞渲染。通过将 CSS 分成多个文件,主要的 阻塞渲染 文件的大小变得更小,从而减少了渲染被阻塞的时间。

在GPU 上呈现动画

浏览器针对处理CSS动画和不会很好地触发重排(因此也导致重新绘制)的动画属性进行了优化。
为了提高性能,可以将被动画化的节点从主线程移到GPU上。
将导致合成的属性包括 3D transforms (transform: translateZ(), rotate3d(),etc.),animating transform 和 opacity, position: fixed,will-change,和 filter。
一些元素,例如<video>, <canvas> 和 <iframe>,也位于各自的图层上。
将元素提升为图层(也称为合成)时,animating transform属性将在GPU中完成,从而改善性能,尤其是在移动设备上。

will-change 属性

will-change 属性告诉浏览器 元素的哪些属性需要修改,使浏览器能够 在元素实际更改之前 设置优化,通过 在实际更改前执行耗时的工作 以提升性能。

will-change: opacity, transform;
font-display 属性

定义了浏览器如何加载和显示字体文件,允许文本 在字体加载或加载失败时 显示回退字体。
可以通过依靠 折中的无样式文本闪现 使文本可见 替代白屏 来提高性能。

@font-face {
  font-family: someFont;
  src: url(/path/to/fonts/someFont.woff) format('woff');
  font-weight: 400;
  font-style: normal;
  font-display: fallback;
}
contain 属性

允许作者指示元素及其内容 尽可能独立于文档树的其余部分。
这允许浏览器针对DOM的有限区域 而不是整个页面 重新计算布局,样式,绘画,大小或它们的任意组合。

https://developer.mozilla.org/en-US/docs/Web/CSS/contain

content-visibility属性

一个具有content-visibility: auto属性的元素可以获得布局、样式和绘制的限制(区域)
如果该元素不在屏幕上(并且与用户无关,则相关元素将是在其子树中具有焦点或已选择的元素),它也会获得大小限制(containment)(并且停止绘制和对其内容进行命中测试)。
简而言之,如果元素不在屏幕上,这不会渲染其后代。浏览器在不考虑元素任何内容的情况下确定元素的大小,在此处则跳过大多数渲染(例如元素子树的样式和布局)。

当元素接近视口时,浏览器不再增加大小限制,而是开始绘制并命中测试元素的内容。这使得渲染工作能够及时被用户看到。

字体

默认情况下,字体请求会延迟渲染树的构造,这可能会导致文本渲染延迟。
可以使用 <link rel="preload"> 覆盖默认行为 并预加载 Web 字体资源。

图片与iframes

大多数这些图像都在屏幕外(非关键),需要用户交互(例如滚动)才能查看它们。

Loading 属性

<img> 元素上的 loading 属性(或 <iframe> 上的 loading 属性)可用于 指示浏览器延迟加载屏幕外的图像/iframe,直到用户滚动到它们附近。

<img src="image.jpg" alt="..." loading="lazy">
<iframe src="video-player.html" title="..." loading="lazy"></iframe>

当优先加载的内容(loading="eager")全部加载完毕时,将触发 load 事件;
那时 visual viewport (可视视口) 中可能存在尚未加载的延迟加载图像,您可以通过检查其布尔值 complete 属性的值 来确定给定图像是否已完成加载。

Intersection Observers 交叉观察者 API

提供了一种 异步观察目标元素 与其祖先元素 或顶级文档视口(viewport)的 交叉状态的方法。祖先元素与视口(viewport)被称为根(root)。

预加载 Preload

将加载和执行分离开,可不阻塞渲染和 document 的 onload 事件。 此节指的是显式预加载指令 ( <link rel=preload>) 而非JS技巧;

  • Preload预加载是声明式的 fetch,页面必定需要的资源,浏览器一定会加载(比如字体),着眼于当前页面的资源,将提升资源加载的优先级;
  • Prefetch 提示浏览器这个资源存在将来需要用到的可能性,着眼于下一个页面资源的预加载,所以在当前页面的优先级是很低的。

缓存

Chrome 有四种缓存: 内存缓存,Service Worker 缓存,HTTP 缓存,和 Push 缓存(H2);
preload 以及 prefetch 都是优先从 HTTP 缓存中获取资源,我们必然要接触很多 304 Not Modified 响应;
获取后,它可以从 HTTP 缓存移动至preloader的内存缓存中。
如果资源可以被缓存(比如说存在有效的cache-control 和 max-age),它被存储在 HTTP 缓存中,
如果资源不能被缓存在 HTTP 缓存中,作为代替,它被放在内存缓存中直到被使用。

Webpack

使用注解开启预取预加载,也可以借助preload-webpack-plugin插件来实现:

import(/* webpackPrefetch: true */ "LoginModal");
import(/* webpackPreload: true */ "ChartingLibrary");

立即生效

preload 支持基于异步加载的标记,使用 的样式表使用 onload 事件立即应用到文档:

<link rel="preload" href="style.css" onload="this.rel=stylesheet">

开启Gzip

使用Node中间件(compression, shrink-ray)和Webpack插件(CompressionPlugin, BrotliWebpackPlugin)

const compression = require('compression');
// const shrinkRay = require('shrink-ray');
...
app.use(compression());
// app.use(shrinkRay());

用WebPack压缩插件BrotliWebpackPlugin作为构建步骤的一部分。否则请使用CompressionPlugin使用 gzip 压缩您的资源。

module.exports = {
	//...
	plugins: [
		//...
		new CompressionPlugin()
	]
}

https://segmentfault.com/a/1190000012571492

使用WebP

WebP 图像比它们的 JPEG 和 PNG 图像小——通常文件大小减少 25-35%。这减少了页面大小并提高了性能。可自选无损压缩和有损压缩。

将图像转换为 WebP

cwebp 命令行工具或Imagemin WebP 插件(npm 包)。
如果您的项目使用构建脚本或构建工具(例如 Webpack 或 Gulp),Imagemin WebP 插件通常是最佳选择.

兼容性

<picture>
  <source type="image/webp" srcset="flower.webp">
  <source type="image/jpeg" srcset="flower.jpg">
  <img src="flower.jpg" alt="">
</picture>

感知性能

除压缩资源和代码优化之外的广阔领域。

骨架屏

在这里插入图片描述

渐进式图像加载

在加载过程中呈现原始图像的轻量级预览,这种图像加载技术被称为 LQIP(低质量图像占位符)
在这里插入图片描述

https://calendar.perfplanet.com/2017/progressive-image-loading-using-intersection-observer-and-sqip/

乐观布局

微调器或进度条。
在这里插入图片描述

伪装即时动画

在这里插入图片描述

开发者工具

性能面板https://developer.chrome.com/docs/devtools/evaluate-performance/

这个话题也是永远都写不完的,参考文献:
https://developers.google.cn/web/fundamentals/performance/rendering/?hl=zh-cn
https://web.dev/rail/
CSDN博客:
https://blog.csdn.net/qweKelliy/article/details/107395628
https://blog.csdn.net/csdnnews/article/details/95267307

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值