Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

前端性能相关 #5

Closed
bowencool opened this issue May 24, 2020 · 5 comments
Closed

前端性能相关 #5

bowencool opened this issue May 24, 2020 · 5 comments
Labels

Comments

@bowencool
Copy link
Owner

bowencool commented May 24, 2020

常见的性能指标

Web 性能领域常见的专业术语 > 以用户为中心的性能指标

TTFB(全称“Time to First Byte”)

表示浏览器接收第一个字节的时间

  • performance.getEntriesByType('navigation')
  • responseStart - fetchStart

DCL

表示 DomContentloaded 事件触发的时间。

  • performance.getEntriesByType('navigation')
  • domContentLoadedEventEnd - fetchStart

L

表示 onLoad 事件触发的时间。

DomContentloaded 事件与 onLoad 事件的区别是,浏览器解析 HTML 这个操作完成后立刻触发 DomContentloaded 事件,而只有页面所有资源都加载完毕后(比如图片,CSS),才会触发 onLoad 事件。

image

  • performance.getEntriesByType('navigation')
  • loadEventStart - fetchStart

FP(全称“First Paint”,翻译为“首次绘制”)

是时间线上的第一个“时间点”,它代表浏览器第一次向屏幕传输像素的时间,也就是页面在屏幕上首次发生视觉变化的时间。

  • performance.getEntriesByName('first-paint')

FCP(全称“First Contentful Paint”,翻译为“首次内容绘制”)

顾名思义,它代表浏览器第一次向屏幕绘制 “内容”(文本,图片,SVG,canvas 元素等)

FMP(全称“First Meaningful Paint”,翻译为“首次有效绘制”)

表示页面的“主要内容”开始出现在屏幕上的时间点。它是我们测量用户加载体验的主要指标。

  • FMP 是一个十分主观的指标,需要开发者自己去测量。像文章一开始说的那样,我们可以在业务逻辑中判断页面加载、渲染完成时记录一个时间戳,然后和 window.performance.timing.fetchStart 相减,来得到 FMP。
  • 捕获FMP的原理 berwin/Blog#42

LCP(全称“Largest Contentful Paint”)

表示可视区“内容”最大的可见元素开始出现在屏幕上的时间点。
image

了解和测量网站真实的性能其实非常困难,像 load 和 DOMContentLoaded 不会告诉我们用户什么时候可以在屏幕上看到内容。而 FP 和 FCP 又只能捕获整个渲染过程的最开始,FMP 更好一点,但是它的算法比较复杂,而且有时候不准。FP 与 FCP 可以让我们知道,我们的产品何时开始渲染;而 FMP 与 LCP 可以让我们了解我们的产品何时“有用”,站在用户的角度,FMP 与 LCP 可以表示我们的产品需要多久才能体现出价值。

注意,这里说的是“有用”,而不是“能用”;那我们如何才能知道我们的产品什么时候“能用”呢?这就需要另一个性能指标“TTI”了。

TTI(全称“Time to Interactive”,翻译为“可交互时间”)

表示网页第一次 完全达到可交互状态 的时间点。可交互状态指的是页面上的 UI 组件是可以交互的(可以响应按钮的点击或在文本框输入文字等),不仅如此,此时主线程已经达到“流畅”的程度,主线程的任务均不超过 50 毫秒。TTI 很重要,因为 TTI 可以让我们了解我们的产品需要多久可以真正达到“可用”的状态。DCL 之后理论上可交互了,但如果此时又执行了 LongTask,页面又卡死了,所以 DCL 并不能代表真实的“可用”状态。
FCP、TTI、FID

FCI(全称“First CPU Idle”)

是对 TTI 的一种补充,TTI 可以告诉我们页面什么时候完全达到可用,但是我们不知道浏览器第一次可以响应用户输入是什么时候。我们不知道网页的“最小可交互时间”是多少,最小可交互时间是说网页的首屏已经达到了可交互的状态了,但整个页面可能还没达到。从名字也可以看出这个指标的意思,第一次 CPU 空闲,主线程空闲就代表可以接收用户的响应了。

FID(First Input Delay)

第一输入延迟。衡量交互性,小于 100ms 为佳。

TBT (Total Blocking Time)

总阻塞时间(TBT)指标衡量了从第一次有内容的绘画(FCP)到交互时间(TTI)之间的,主线程被阻塞到足以阻止输入响应性的总阻塞时间。
image

getObserver('longtask', (entries) => {
    entries.forEach((entry) => {
      // get long task time in fcp -> tti
      if (entry.name !== 'self' || entry.startTime < fcp) {
        return;
      }
      // long tasks mean time over 50ms
      const blockingTime = entry.duration - 50;
      if (blockingTime > 0) tbt += blockingTime;
    });
  });

CLS (Cumulative Layout Shift)

累计布局偏移(CLS)是一个重要的、以用户为中心的衡量视觉稳定性的指标,因为它有助于量化用户经历意外布局偏移的频率 --较低的 CLS 有助于确保页面令人愉悦。

image
CLS=影响分数✕距离分数
75%✕25%=0.1875

@bowencool bowencool changed the title 性能指标 前端性能指标 May 24, 2020
@bowencool
Copy link
Owner Author

1.性能优化 (RAIL模型)

参考链接: https://developers.google.com/web/fundamentals/performance/rail?hl=zh-cn

使用RAIL模型评估性能:

  • 响应(Response):在 100 毫秒以内响应。
  • 动画(Animation):设置动画或滚动时,在 10 毫秒以内生成帧。
  • 空闲(Idle):最大程度增加空闲时间。
  • 加载(Load):在 1000 毫秒以内呈现内容(关键渲染路径)。

1.1响应

在100ms内响应用户操作,他们会觉得可以立即获得结果。时间再长,操作与反应之间的连接就会中断。对于需要超过 500 毫秒才能完成的操作,始终提供反馈。

1.2动画(渲染性能)

目前大多数设备的屏幕刷新率为60Hz,所以尽可能使动画保持60fps。由于浏览器有整理工作要做,因此每一帧的工作需要在 10 毫秒内完成。

  • 优化 JavaScript 执行
    • 对于动画效果的实现,避免使用setTimeoutsetInterval,请使用requestAnimationFrame
    • 将长时间运行的 JavaScript 从主线程移到 Web Worker。
    • 使用微任务来执行对多个帧的 DOM 更改。
  • 缩小样式计算的范围并降低其复杂性
    • 降低选择器的复杂性,比如BEM命名规范。
    • 减少在元素更改时需要计算的工作量。
  • 避免大型、复杂的布局和布局抖动
    • 布局的作用范围一般为整个文档。
    • DOM 元素的数量将影响性能;应尽可能避免触发布局。
    • 避免强制同步布局和布局抖动;先读取样式值,然后进行样式更改。
  • 简化绘制的复杂度、减小绘制区域
    • transformopacity属性之外,更改任何属性始终都会触发绘制。
    • 通过层的提升和动画的编排来减少绘制区域。
  • 坚持更改可以由合成线程单独处理的属性、控制层数
    • 坚持使用transformopacity属性更改来实现动画。
    • 使用will-changetranslateZ提升移动的元素(推荐使用will-change: transform;) 。
    • 避免过度使用提升规则;各层都需要内存和管理开销。
  • 使输入处理程序去抖
    • 避免长时间运行输入处理程序;它们可能阻止滚动。
    • 不要在输入处理程序中进行样式更改(将触发强制同步布局)。
    • 使滚动处理程序去除抖动。

1.3空闲

用户没有与页面交互,但主线程应足够用于处理下一个用户输入。如果用户开始交互,优先级最高的事项是响应用户。例如,往页面里插入10,000个dom,如果直接插入,势必造成主线程阻塞、页面假死,要实现小于 100 毫秒的响应,应用必须在每 50 毫秒内将控制返回给主线程,可以每隔50ms插入100个dom,既可以完成任务,又能确保即时的响应。

1.4加载(关键渲染路径)

为了尽快完成关键渲染路径,应当最大限度减小三种可变元素:关键资源的数量、大小,关键路径长度。具体如下:

  • 减少关键资源的数量:删除它们、延迟它们的下载(懒加载),将它们标记为异步等。
  • 减小传送大小:压缩代码,利用缓存等。
  • 优化关键资源的加载顺序:尽可能提早下载(预加载),以缩短关键路径长度。

@bowencool bowencool changed the title 前端性能指标 前端性能相关 May 24, 2020
@bowencool
Copy link
Owner Author

bowencool commented May 24, 2020

白屏问题(优化关键渲染路径)

参考:https://juejin.im/post/5d00820b5188255ee806a1c7

分析:白屏时间内发生了什么?

  • 用户按下回车,浏览器解析网址,DNS查询,通过IP发生HTTP请求。
  • 服务端返回HTML,浏览器解析HTML,并在此过程中加载css和js。
  • 执行js,创建各种DOM并渲染到根节点,直到第一个可视元素出现。

从以下维度去优化

  • 过大的文件会拖慢加载速度。对应的解决方案就是压缩、拆包、treeshaking、差异化加载(es6+和es5双份资源,动态polyfill)、缓存等。
  • Chrome的并发限制数量为6,所以关键资源的数量过多也会加剧白屏时间。对应的解决方案就是删除、延迟、标记为异步、使用用http2等。
  • 页面直出。大部分web应用的可视元素都是由js执行创建插入的,这个过程很明显是可以提前的。手段有服务端渲染、预渲染等。也可以给应用添加骨架屏、Loading指示等,也算是预渲染。
  • 当然还有路由跳转之间的优化,可以开启preload、dnsprefetch、keep-alive等。

@bowencool
Copy link
Owner Author

bowencool commented May 24, 2020

JavaScript 动画和 CSS 动画该如果抉择

动画优化思路

参考链接:husky-dot/xiaozhi#13

  • 首先,动画一般路径为:js -> style -> layout -> paint -> composite
  • 如果 CSS 动画只是改变transformsopacity,这时整个 CSS 动画得以在 合成线程 完成(而JS动画则会在 主线程 执行,然后触发合成线程进行下一步操作),在 JS 执行一些昂贵的任务时,主线程繁忙,CSS 动画由于使用了合成线程可以保持流畅。
  • 如果有任何动画触发绘制,布局或两者,则需要 “主线程” 才能完成工作。 这对于基于 CSS 和 JavaScript 的动画都是如此。

所以,在实现一些小的交互动效的时候,就多考虑考虑 CSS 动画。对于一些复杂控制的动画,使用 javascript 比较可靠。

@bowencool
Copy link
Owner Author

bowencool commented Jul 26, 2020

webpack构建优化

参考链接:https://juejin.im/post/5b5d6d6f6fb9a04fea58aabc

  • 当模块内容未改变时,文件名应当稳定。(持久化缓存,webpack 5 已优化)
  • 压缩代码、图片等。小图使用data uri内联。
  • 使用ES module以便于tree-shaking
  • 优化拆包策略:可以从共用率、使用频率、升级频率等维度去划分。
  • 构建速度提升可以选用:cache-loader(缓存编译结果到磁盘上,实测提升30%)、happypack(没选用的原因是,构建过程中最耗时的是压缩代码,压缩代码的工具本身就支持并行,其他任务优化空间太小,没必要)、DllPlugin(提前构建部分代码,实测提升47%)等。
  • 多线程构建
  • 使用更快的编译器,比如 esbuild
  • 开发时使用 bundleless 方案,比如 vite
  • 开启 webpack 5 的 Module Federation,分治思想

webpack 流程

image

延伸阅读:https://juejin.cn/post/6943468761575849992#heading-2

@bowencool
Copy link
Owner Author

bowencool commented Feb 12, 2022

CLS 优化

偏移基本上都是发生了 re-layout,所以这个问题跟 layout 有很大的相关性

  • 总是指定元素尺寸
  • 总是为嵌入元素预留空间
  • 避免在已存在的内容上方插入新内容,除非为了响应用户交互或者有预留空间
  • 自定义字体:
    • 看情况使用 font-display: optional
    • unicode-range 优先下载高频使用的字符
    • preload
  • 动画优先考虑 transform

bowencool pushed a commit that referenced this issue Jan 6, 2024
chore: update github-slugger
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant