前端性能优化及其度量方法
前端页面性能对用户留存、用户直观体验有着重要影响,当页面加载时间超过 2 秒后,加载时间每增加一秒,就会有大量的用户流失,所以做好页面性能优化,对网站来说是一个非常重要的步骤。
提到性能优化,灵魂三问来啦,什么是前端性能优化?如何度量前端性能?如何做性能优化?一个页面的性能指标非常多,面对一大堆性能指标,可能一个老手也一时间不知道从何开始分析,所以本篇文章我会介绍一些性能优化最基础的知识点和工具,以及根据工作分析出数据如何做性能优化,并推荐几个公司个性化需求改造后的性能检测工具。
性能优化及其度量方法
- 优化技术及标准的发展
- 以用户为中心的指标
- 如何做性能优化
- 前端性能分析工具
Terminology优化名词解释
- PWA: Progressive Web Apps, 渐进式Web应用开发,旨在增强 Web 能力,缩小与原生应用的差距并创建与其类似的用户体验
- Lighthouse: 自动化测试工具, 用于测试页面的性能并提出优化建议
- SSR: Server Side Rendering, 在服务端请求数据并组装HTML的渲染方式
- NSR: Native Side Rendering, 在客户端提前请求数据并组装HTML的渲染方式
- Rehydration: [,riːhaɪ’dreɪʃən] 俗称“注水” , 复用服务端渲染生成的 DOM 结构及数据,并执行事件绑定逻辑来启动页面的过程
- RALL模型,Google RAIL 模型提供了一个思考性能问题的方法论
- Navigation Timing :定义的是文档导航过程中完整的性能度量,即文档从发起请求到完成加载的各阶段耗时
- Resource Timing: 记录的是子资源从请求到加载完成各阶段的耗时,了解网络下载资源的阶段至关重要。这是修复加载问题的基础
- TTI (Time to Interactive):页面可交互时间
优化技术及标准的发展
HTTP/1 有连接无法复用、队头阻塞、协议开销大和安全因素等多个缺陷
HTTP/2 通过多路复用、二进制流与 Header 压缩等技术,极大地提高了性能,但是还是存在一些问题
HTTP/3 抛弃 TCP 协议,以全新的视角重新设计 HTTP。其底层支撑是 QUIC 协议,该协议基于 UDP,有 UDP 特有的优势,同时它又取了 TCP 中的精华,实现了即快又可靠的协议
http发展史:
RALL模型
:
Google RAIL 模型提供了一个思考性能问题的方法论。
RAIL将用户体验分解为几个关键动作(如点击,滚动,加载),rail模型为这些动作制定了性能目标,RAIL代表的是web应用生命周期内的四个不同的方面。参考:https://web.dev/rail/用户在采取操作后,需要在100ms内收到反馈,用户才不会有延迟感。16ms则对应60fps的每帧处理时间,60fps是动画基本流畅需要达到的帧率。当动画过程出现卡顿或者中断时,用户通常会感觉不爽。
100ms 响应用户输入 每帧动画在16ms内完成 最大化空闲时间 3G 中等设备 5s内可交互
前面提到:
事件处理要求在50ms完成,延时任务执行不能超过50ms
为什么是50ms?
主线程除了处理用户事件,也有其他任务执行,这些任务会占用部分时间,事件处理会排在后面执行。我们的目标是在100ms内对用户事件做出响应,结合图上的任务情况,100ms内需要执行两个任务,分到每个任务就是50ms。
PWA
Web应用在用户体验上往往不如Native应用。首先 Web应用往往依赖网络来加载内容,存在弱网环境加载慢,离线情况无法访问等问题,其次 Web应用也无法添加到桌面,用户需要通过输入url来获取内容。除此之外,Native应用的部分能力在Web也是缺失的,比如消息推送能力。Native应用虽然体验好,但也存在开发成本高、动态性差等问题,用户在使用前需要下载安装。
基于这个背景,谷歌在2016年提出PWA的概念,希望通过增强 Web 能力,缩小与 Native 应用的差距并提供与其类似的用户体验。
PWA有几个重要的特性:
- ServiceWorker: sw可以看成一个可编程网络代理,提供了离线化支持,其中就包括缓存和预加载,sw也是其他PWA特性实现的基础
- APP Manifest: 用来定义 Web 应用的表现和行为,包括桌面图标、闪屏动画、全屏浏览等
- Push & Notification: 为Web应用补齐了消息推送和接收能力
- 离线缓存: 借助sw的离线化能力,用户在离线的情况也能使用部分功能
当然,PWA也包含了其它特性,如读取设备状态、蓝牙分享等,最终的目的都是希望通过渐进增强的方式来逐步达到Native App的体验。
参考:https://whatwebcando.today/ https://web.dev/progressive-web-apps/ https://github.com/GoogleChrome/workbox/
PWA (渐进式Web应用)
目前主流浏览器( Chrome,Safari,Firefox,Edge) 都不同程度上支持了 PWA。国内外一些网站也已进行了 PWA 实践,
alibaba.com
, 京东等。这些网站在应用 PWA 后也得到了一些可量化的收益。根据谷歌分享的案例,京东印尼站,在使用PWA的缓存、桌面安装、消息推送等能力,转化率提升了53%。https://web.dev/jdid/
Corel Corporation: PWA users are 2.5x more likely to purchase Gravit Designer PRO 12 https://web.dev/gravit-designer/;加拿大的一家公司(科立尔数位科技公司),使用PWA的用户更倾向于购买他们提供的产品。
参考
https://whatwebcando.today/
https://web.dev/progressive-web-apps/
https://github.com/GoogleChrome/workbox/
标准组织
标准的制定离不开标准组织,性能标准也不例外。性能领域有两个重要组织:
一个是1994年成立的W3C,W3C 是 Web 技术领域最具权威和影响力的国际中立性标准机构
另一个是W3C在2010年成立的Web性能工作组,Web 性能工作组的目标就是制定 衡量Web应用性能的方法和 API
- 1994年 W3C (World Wide Web Consortium) 成立
- 2010年 W3C 成立了 Web 性能工作组
标准的发展
这些API可以大致分为3大类,
- 第一类是框架类API:主要包含最左边部分,High Resolution time, performace timingline,提供的高精度时间接口,以及查询性能数据的接口
- 第二类是度量类API: 用来检测页面生命周期内不同方面的性能数据, 对应中间的各种 Timing API 以及 Long Tasks API
- 第三类是优化策略API: 用来改善页面性能,主要分布最右边,提供 页面可见性,任务调度,预加载等能力
参考:
https://www.w3.org/PM/Groups/repositories.html?gid=45211
Element Timing: https://github.com/WICG/element-timing
https://github.com/WICG/layout-instability
Performance Timeline
Performance Timeline 包含几个部分
-
navigation-timing
:navigation of the document 高精度时间 -
resource-timing
:页面资源 获取各类性能数据的查询接口 -
user-timing
:开发者自定义的一些监控, 主要是(mark 和 measure,下文会讲)两个基础类
- 其中PerformanceEntry是其他Timing Entry的基类,通过getEntries 返回的Entry列表,都继承自PerformanceEntry,每个performaceEnrty
- PerformanceObserver 用于基于事件的指标测量
浏览器支持的entryTypes类型可以通过PerformanceObserver.supportedEntryTypes 提供的接口来查询
High Resolution Time
- performance.now()
- performance.timeOrigin
- performance.toJSON()
三个接口
- getEntries()
- getEntriesByType()
- getEntriesByName()
两个对象
- PerformanceEntry
- PerformanceObserver
new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
console.log('LCP candidate:', entry.startTime, entry);
}
}).observe({type: 'largest-contentful-paint', buffered: true});
Navigation Timing
Navigation Timing 定义的是文档导航过程中完整的性能度量,即文档从发起请求到完成加载的各阶段耗时
- navigationStart 加载起始时间
- redirectStart 重定向开始时间(如果发生了HTTP重定向,每次重定向都和当前文档同域的话,就返回开始重定向的fetchStart的值。其他情况,则返回0)
- redirectEnd 重定向结束时间(如果发生了HTTP重定向,每次重定向都和当前文档同域的话,就返回最后一次重定向接受完数据的时间。其他情况则返回0)
- fetchStart 浏览器发起资源请求时,如果有缓存,则返回读取缓存的开始时间
- domainLookupStart 查询DNS的开始时间。如果请求没有发起DNS请求,如keep-alive,缓存等,则返回fetchStart
- domainLookupEnd 查询DNS的结束时间。如果没有发起DNS请求,同上
- connectStart 开始建立TCP请求的时间。如果请求是keep-alive,缓存等,则返回domainLookupEnd
- (secureConnectionStart) 如果在进行TLS或SSL,则返回握手时间
- connectEnd 完成TCP链接的时间。如果是keep-alive,缓存等,同connectStart
- requestStart 发起请求的时间
- responseStart 服务器开始响应的时间
-
domLoading
:这是整个过程的起始时间戳,浏览器即将开始解析第一批收到的 HTML 文档字节。 -
domInteractive
:表示浏览器完成对所有 HTML 的解析并且 DOM 构建完成的时间点。 -
domContentLoaded
:表示 DOM 准备就绪并且没有样式表阻止 JavaScript 执行的时间点,这意味着现在我们可以构建渲染树了。-
许多 JavaScript 框架都会等待此事件发生后,才开始执行它们自己的逻辑。因此,浏览器会捕获
EventStart
和
EventEnd
时间戳,让我们能够追踪执行所花费的时间。
-
许多 JavaScript 框架都会等待此事件发生后,才开始执行它们自己的逻辑。因此,浏览器会捕获
-
domComplete
:顾名思义,所有处理完成,并且网页上的所有资源(图像等)都已下载完毕,也就是说,加载转环已停止旋转。 -
loadEvent
:作为每个网页加载的最后一步,浏览器会触发
onload
事件,以便触发额外的应用逻辑。 - unloadEventStart unload事件触发的时间
- unloadEventEnd unload事件执行完的时间
- performance.timing:页面导航性能可以通过 performance.timing 获得
- performance.navigation.type:NavigationType表示导航的类型,目前有四种类型,分别表示导航、刷新、前进后退、预渲染
- performance.navigation.redirectCount:从时间轴可以看到,navigation timing复用了 Resource Timing的时间轴,即图中橙色部分
enum NavigationType {
"navigate",
"reload",
"back_forward",
"prerender"
};
Resource Timing
Resource Timing 记录的是子资源从请求到加载完成各阶段的耗时,了解网络下载资源的阶段至关重要。这是修复加载问题的基础。
- 了解资源时序的阶段。
-
知道每个阶段提供给
Resource Timing
(资源时序)API。 - 在时间轴图表中识别性能问题的不同指示。如连续的透明条或大块绿色。
所有网络请求都被视为资源。当它们通过网络检索时,分为不同的生命周期。
Network
(网络)面板使用的
Resource Timing API
和提供给开发者的API是一样的。
注意:
当使用跨源资源的
Resource Timing API
时, 请确保所有资源都有CORS头信息。
Resource Timing API提供了关于每个单独资源接收时间的详细信息。 请求生命周期的主要阶段是:
-
Redirect
(重定向)
-
立即开始
startTime
。 -
如果发生重定向,
redirectStart
也会开始计时。 -
如果重定向发生在此阶段结束时,那么
redirectEnd
将被采用。
-
立即开始
-
App Cache
(应用程序缓存)
-
如果浏览器有缓存,将采用
fetchStart
时间。
-
如果浏览器有缓存,将采用
-
DNS
-
domainLookupStart
记录DNS请求开始的时间。 -
domainLookupEnd
记录DNS请求结束的时间。
-
-
TCP
-
connectStart
记录开始连接到服务器的时间。 -
如果用了TLS或SSL,
secureConnectionStart
记录开始连接时间。 -
connectEnd
记录连接完毕时间。
-
-
Request
(请求)
-
requestStart
记录请求发送到服务器的时间。
-
-
Response
(响应)
-
responseStart
记录最开始的响应时间。 -
responseEnd
记录响应结束时间。
-
子资源的性能数据可以通过指定 entryType 为 resource 来查询,
performance.getEntriesByType('resource')
需要注意的是:对于跨域资源的性能数据,需要正确返回 timing-allow-origin: * 才能被页面采集到
Paint Timing
-
收集 FP, FCP 两个指标
-
name: first-paint 首次绘制
-
name: first-contentful-paint 首次内容绘制
var observer = new PerformanceObserver(function(list) {
var perfEntries = list.getEntries();
for (var i = 0; i < perfEntries.length; i++) {
Console.log(perfEntries.length[i]);
}
});
observer.observe({entryTypes: ["paint"]});
Event Timing
-
WICG
的一个提案 - 跟踪用户输入的处理延迟
Event Timing 提出的背景是需要跟踪输入事件的处理延迟,目前处于草案阶段;可以用来计算 首次输入延迟(FID) 这个指标,算法也比较简单,开始处理时间-接收时间得到;参考:https://wicg.github.io/event-timing/
Frame Timing
Frame Timing,用来记录一些慢帧信息,目前处于提案阶段:https://wicg.github.io/frame-timing/
- 其中一帧定义为两个vsync之间的时间间
- 在60HZ刷新率的屏幕下,每帧的时间上限是16ms
new PerformanceObserver((list) => {
const perfEntries = list.getEntries();
for (let i = 0; i < perfEntries.length; i++) {
// Process slow-frame notifications
console.log("Uh oh, slow frame: ", perfEntries[i]);
}
});
observer.observe({entryTypes: ['frame']});
Long Tasks API
Long Tasks API,用来监控主线程的长任务 : Long task > 50ms
- 长任务会阻塞主线程,导致无法快速响应用户输入。对于长任务,一般的手段是通过合理拆分成子任务来优化
- 通过 PerformanceObserver 来采集
var observer = new PerformanceObserver((list) => {
var perfEntries = list.getEntries();
for (var i = 0; i < perfEntries.length; i++) {
// Process long task notifications:
// report back for analytics and monitoring
// ...
}
});
observer.observe({entryTypes: ["longtask"]});
Navigation Timing | 页面导航性能 |
---|---|
Resource Timing | 子资源加载性能 |
Paint Timing |
FP/FCP |
Element Timing |
自定义 LCP |
Event Timing |
FID |
Layout Instability API |
CLS |
Long Tasks API | 长任务优化 |
Frame Timing | 慢帧优化 |
以用户为中心的指标
标准API的介绍,目的:如何检测应用的性能,如何去发现问题并有针对性地实施优化。对于用户的真实感受,我们还缺少一个可以量化的标准去衡量。🤔 如何更准确地评估用户的真实感受?
用户指标概览
从用户角度来看一个网页的加载过程:
- 首先用户关注的一定是网页内容是不是呈现得足够快。如果页面加载太慢,用户往往会失去耐心而离开
- 接着用户在看到关注的内容呈现之后,用户会下意识去操作页面。这个时候如果页面没有及时给出反馈,那么用户会察觉到我们的页面有延迟
- 最后,如果网页内容在呈现过程中出现较大的抖动,比如页面突然啪的一下出来,或者视窗内内容发生了较大偏移,这个时候用户的感受往往是不愉悦的
因此我们在制定指标的时候,需要能够反映用户以上三个方面的感受,概括来说就是用户可感知的加载速度、页面的响应能力以及内容呈现的平滑度。
绿色部分的两个指标,分别是首次内容绘制时间和最大内容绘制时间,反映的是页面的加载速度,以用户看到有效内容绘制为衡量标准。
蓝色部分有三个指标,分别是 首次输入延迟、页面可交互时间、主线程累计阻塞时间,反映的则是页面的响应能力。其中FID需要用户事件来触发,所以一般应用在生产环境。在实验室环境我们一般使用 TBT 或者 TTI 来代替。
橙色部分,CLS 指的是页面生命周期内累计布局偏移,反映的是页面内容呈现的平滑度,这个指标以分数的形式提供,可以在生产环境和实验室环境测试得到。
- FCP 首次内容绘制时间 (First contentful paint)
- LCP 最大内容绘制时间 (Largest contentful paint)
- FID 首次输入延迟 (First input delay)
- TTI 页面可交互时间 (Time to Interactive)
- TBT 主线程累计阻塞时间 (Total blocking time)
- CLS 累计布局偏移 (Cumulative layout shift)
FCP (First contentful paint) , LCP(Largest contentful paint)
FCP是首次内容绘制,LCP最大内容绘制
FCP相对简单,只要页面有内容出现,我们就认为达到了首次内容绘制时间
而最大内容绘制时间需要不断跟踪页面的渲染情况。实际检测的时候,浏览器会把检测到的最大元素以事件的形式持续发给页面,所以最大内容时间的检测,一般以页面接收到的最后一个事件为主。
Largest Content
1. <img />
2. <svg><image/></svg>
3. <video poster=“poster.gif"></video>
4. <div style='background:url()' />
5. <div><text/></div>
new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
console.log('LCP candidate:', entry.startTime, entry);
}
}).observe({type: 'largest-contentful-paint', buffered: true});
目前最大内容可以是图片和文字,其中图片包括img标签、svg image、video 封面、背景图。
说明:Element Timing API 提拱了 LCP 的检测支持
TTI (Time to Interactive)
TTI 是页面可交互时间。ta的检测机制比较复杂,需要同时考虑主线程和网络情况。
图的上面是网络加载情况,底下主线程的任务分布情况,其中橙色代表的是长任务:https://web.dev/tti/
- 首先以FCP作为起点,向右查找
- 找到一个 5s 的时间窗口,这个时间窗口内没有长任务, 并且网络请求不多于两个,图中灰色部分就是我们要找的时间窗口
- 以时间窗口的起点往回找长任务,这个长任务的结束时间就是页面可交互时间
- (如果没有长任务,则在FCP停止查找,此时认为FCP与TTI重叠)
FID (First input delay), TBT (Total blocking time)
FID 是首次输入延迟,代表的是用户对于页面响应度的第一印象。首次输入延迟指的是主线程首次接收到用户输入到开始响应的时间。输入延迟往往发生在主线程繁忙的时候,常见的场景是用户与页面交互时,主线程正在解析并执行一个巨大的JS,这个时候主线程无法响应用户。
FID 主线程首次接收用户输入到开始响应的时间:
TBT 计算公式:
T
B
T
=
t
o
t
a
l
(
l
o
n
g
t
a
s
k
)
−
c
o
u
n
t
(
l
o
n
g
t
a
s
k
)
∗
50
TBT=total(longtask)-count(longtask)*50
T
B
T
=
t
o
t
a
l
(
l
o
n
g
t
a
s
k
)
−
c
o
u
n
t
(
l
o
n
g
t
a
s
k
)
∗
5
0
任务取样区间为 FCP到TTI
TBT 是主线程累计阻塞时间,在实验室环境一般作为FID的替代指标。计算规则 取FCP到TTI之间的所有任务,累计长任务超出50ms的部分。
new PerformanceObserver((entries, observer) => {
const firstInput = entries().getEntries()[0];
const inputDelay = firstInput.processingStart - firstInput.startTime;
observer.disconnect();
}).observe({type: 'first-input', buffered: true]});
FID 可以通过Event Timing来收集。
CLS(Cumulative layout shift)
CLS 累计布局偏移。视窗内可见元素的起始位置发生变化,就是发生了一次布局偏移,累计布局偏移则需要把所有非预期的布局偏移分数累加。
布局偏移分数 = 影响范围分数*位移分数
影响范围分数=影响范围(红色部分)/视窗面积
位移分数=位移(紫色箭头)/视窗高度
- Impact Fraction: 0.75
- Distance Fraction: 0.25
- layout shift score: 0.75*0.25=0.1875
let cls = 0;
new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
if (!entry.hadRecentInput) {
cls += entry.value;
}
}
}).observe({type: 'layout-shift', buffered: true});
Web Vitals
- Web Vitals 的提出,是希望为应用性能提供统一的度量标准
- Core Web Vitals 是Web Vitals 最核心的部分,包含 LCP、FID、CLS三个指标
- ⚠️谷歌计划在2021年将 Core Web Vitals 列为网页排名算法中新的因子
为了简化指标的采集,谷歌也提供了web-vitals库。Core Web Vitals,浏览器对其的支持还不完善,目前仅在最新的Chromium支持比较完备。浏览器支持情况 https://github.com/GoogleChrome/web-vitals#browser-support
import {getCLS, getFID, getLCP} from 'web-vitals';
getCLS(console.log);
getFID(console.log);
getLCP(console.log);
getFCP(console.log);
最新的Devtools也增加了对Web Vitals的测试支持
打开一个空白页,使用F12打开开发者工具,选择performance面板
接着勾选截图和web vitals选项,开始录制
输入测试url,等待加载完成,停止录制
可以看到,最新的devtools
- web vitals、long tasks都有了独立的窗格
- LS 布局偏移
- 时间线上,增加了LCP指标等
举个政采云的前台模型衡量指标来说明
https://juejin.cn/post/6887580440803311630
如何做性能优化
一般来说,用户可以感知的性能包括加载速度、响应能力 动画流畅性 能耗 内存占用等。对于能耗和内存占用,在web性能优化时没有特别好的技术手段,因此最起码的,我们要做到让手机不发烫、应用不奔溃。
常用的优化手段
一般来说 缓存技术和预加载技术是性能优化最有效最直接的手段。除此之外,在不同的场景使用不同的渲染方案,也是常用的场景化优化手段。
-
缓存技术
-
预加载技术
-
渲染方案
优化原则
-
网络是不可靠的,尽量避免或提前
- 如果能解决网络的问题,也就解决了性能的大部分问题
-
JS 是单线程的, 利用 Worker 并行
- 在浏览器环境可能是Web Worker, sw,在端上使用性能更好的js worker
-
开发端的能力, 极致优化
- 利用服务端发起请求并渲染数据;客户端可以提供离线能力、以及 JS Worker
-
在真实环境上测试性能:
- 举一个v8的例子:v8在之前使用基准测试来度量性能,导致出现了一些在真实场景下没有任何帮助,甚至起到反作用的优化。可见我们在真实环境上测试性能的重要性
2.
缓存技术
缓存主要有CDN、浏览器缓存、应用离线包。
1.通用优化:
- 使用CDN加速静态资源
- 合理设置缓存时间 Cache-Control
- 避免注册unload以影响前进后退缓存
2.前端最佳实践
- 使用异步的Cache API/IndexDB,
- Cache API主要存储HTTP请求,其他存储IndexDB
- 尽量避免同步的 LocalStorage, ~5M
3.基于端的优化
- 提供离线包,提前下发资源
- 进一步考虑将离线包的资源预置进HTTP Cache
- 进一步考虑主文档、关键路径资源预置到Memory Cache
浏览器缓存比较多,其中
- V8 CodeCache是字节码缓存, 可以大大减少JS解析、编译的耗时
- BFCache: 用来缓存整个页面的状态,主要应用在前进后退导航、预渲染
- Network Cache: 网络相关的缓存,如DNS缓存
- 虚线部分是前端缓存:indexdb cookie local storage 等
- 除此之外,还有浏览器使用最广泛的缓存HTTP Cache,内存缓存等
- 其他浏览器缓存: 如图片解码数据,可以在惯性滚动及时把解码数据绘制到屏幕
-
其他:
- https://medium.com/dev-channel/the-cost-of-javascript-84009f51e99e
- Promise IndexDB https://www.npmjs.com/package/idb
3.
预加载技术
预加载技术主要是标准定义的Resouces Hints和Preload。其中 Resources Hints包括 dns-prefetch, preconnect, prefetch, or prerender;Preload不在Resources Hints的定义里面
<link rel="dns-prefetch" href=“//example.com">
<link rel="preconnect" href="//example.com">
<link rel="preconnect" href="//cdn.example.com" crossorigin>
<link rel="prefetch" href="//example.com/next.html" as=“document" crossorigin="use-credentials">
<link rel="prefetch" href="/library.js" as="script">
<link rel="prerender" href="//example.com/next-page.html">
<link rel="preload" href="/styles/other.css" as="style">
preload prefetch的区别
- preload 用于预加载当前页面的关键资源
- Prefetch 预加载的是下一个页面的资源
prerender
- 预渲染会加载文档及子资源, 因此只预加载用户最大几率访问的页面
4.
渲染方案
常见的web渲染方案,按照渲染的环境和时机,可以分为四大类,分别是SR CSR SSR 以及在阿里业务大量使用的NSR。
- 静态渲染 Static Rendering (SR)
- 前端渲染 Client Side Rendering (CSR)
- 服务端渲染 Server Side Rendering (SSR)
- 客户端渲染 Native Side Rendering (NSR)
每种渲染方式都有其适用范围。参考:Rendering on the Web https://developers.google.com/web/updates/2019/02/rendering-on-the-web
SR VS CSR VS SSR VS NSR
SR
:SR 编译阶段就生成好html结构。页面加载之后就可以直接进行渲染,性能最好,一般只能用在静态页面上。
CSR
:CSR 是最常见的渲染方式之一,广泛在SPA应用使用。CSR 通常在前端发起请求,在数据返回时使用JS渲染出HTML。在合理进行代码分包的前提下,其性能主要取决于网络和设备性能。在中低端设备和弱网环境下性能往往较差。
服务端渲染SSR
通常在服务端直接渲染出HTML的方式一般都可以认为是SSR
- 传统SSR使用PHP、Java等服务端语言结合后端模板来生成HTML结构,在前后端分离的大背景下,这种渲染方式已经不是主流。
- 主流的服务端渲染,使用的是前后端同构的方案, SSR with Rehydrate
- 比CSR相比,首屏性能和可交互时间更短,在低端设备和弱网情况下表现也更好。
- 较长的首字节时间,SSR渲染会增加服务器成本, 可以考虑增加一层缓存
NSR
- NSR 可以看作客户端的SSR,原理是把 SSR 放在服务端的工作放到端上来执行, 另外也配合客户端的离线资源和数据预取来进一步提升性能
- NSR可以把WebView的初始化、框架JS的执行与数据请求和渲染并行,大大缩短首屏时间,NSR在UC信息流、手淘会
方案对比
每种渲染方案都有一定优势,也有其局限性和缺点,我们需要根据实际场景来选择合适的渲染方案。
SR |
CSR |
SSR with Rehydrate |
NSR |
|
---|---|---|---|---|
简介 | 编译阶段生成HTML结构 | 所有逻辑在前端完成, 最常见的SPA | 在服务端渲染首屏,在前端完成事件绑定 | 客户端的SSR, 可以提前缓存资源和发起请求 |
优势 | 👍 TTFB/FCP/TTI 👍 流式渲染 | 👍 动态性 👍 TTFB | 👍 动态性 👍 首屏快 | 👍 动态性 👍 缓存/并行 |
缺陷 | 👎 动态性 | 👎 TTI >>FCP 👎 流式 | 👎 TTFB/TTI 👎 服务器成本 | 👎 架构复杂 👎 需要端能力支持 |
适用场景 | 博客、文档 | 端外投放 移动设备/弱网 | 端内投放 |
前端性能检测的两种方式
总结:
SR性能最好,一般只用在静态页面上
CSR的性能则取决于设备性能和网络状况,弱网、中低端设备性能很差
SSR with Rehydrate 利用服务端良好的网络和性能大大提升了首屏性能,在低端设备也有较好表现。适合作为通用方案投放在端外
NSR 利用端的缓存和数据预取能力,使得应用初始化与首屏渲染可以并行,是除SR之外性能最好的渲染方式。另外由于是在端上渲染,没有额外增加服务器预算。
其他优化手段
移动端的场景,WebView复用可以大大减少WebView的初始化时间
在一些很难优化的场景,使用人文关怀:苹果手机不是性能最好的,但是体验却是最好的
关于如何优化前端性能的其他资料:Google、百度一搜一大堆一大堆的,就不附上没用的链接了
性能监测工具
如何使用 Timeline 工具:https://developers.google.com/web/tools/chrome-devtools/evaluate-performance/timeline-tool?hl=zh-cn
任务指什么?
了解任务的定义
- 如图,火焰图最顶端灰色的矩形,就是在主线程执行的一个个的任务
- 任务是一个个的工作单元,可以是js执行、html解析、样式计算、排版等
- 任务右上角带有红色三角形表示的是超过50ms的长任务
背景知识,关于主线程
现代浏览器采用多进程架构,一般分为Browser/UI进程、Render/Web进程
Chrome的主线程
在Browser进程,UI线程
在Render进程,Blink主线程
Lighthouse
Llighthouse的工作机制,Lighthouse与浏览器使用Devtools协议通信。
Lighthouse 关注四个组件:
- Driver: Devtools协议的封装,通过协议可以对页面实现导航、执行脚本、监听网络等各种操作
- Gatherers: 日志收集器,收集 trace、网络事件、页面数据等各类日志
- Audits: 检测项目,对日志进行分析,返回检测结果
- Report: 按各个检测项配置的权重计算得分,输出测量的指标数据和优化建议
使用 Lighthouse测试应用页面性能
Lighthouse 是一个开源的自动化工具,用于改进网络应用的质量。 它可以作为一个 Chrome 扩展程序运行,或从命令行运行。 提供一个要审查的网址后,它将针对此页面运行一连串的测试,然后生成一个有关页面性能的报告。
通过生成的性能报告, 会给出一些建议用于提升应用的性能
分为五个部分进行统计并给出打分,分值越高越好。五个部分分别为
- 性能
- 无障碍
- 最佳体验
- SEO
- PWA
每个部分会给出检测规则对应的问题和改进意见。
- 移动端有很多H5业务
- 需要在真实环境上测试性能
- Lighthouse在移动端支持不足
所以各个公司对Lighthouse进行扩展来满足业务诉求:
美团的性能检测/测试
-
RAPTOR
: Raptor是面向基础设施和端到端应用程序的监控平台 - CAT:CAT 分布式实时监控平台
- FE GUARD:一套前端线上质量监控系统,致力于保障系统的稳定性、健壮性
- Falcon系统,Avatar系统等
政采云百策
:https://juejin.cn/post/6887580440803311630
业界还有很多优化方案,不一一列出
阿里的性能监控/测试
- 实验室测试:lighthouse webpagetest
- 线上监控: Chrome UX Report(CrUX) 阿里云的应用实时监控服务arms、uc的岩鼠 https://yanshu.effirst.com/ 、ARMS https://cn.aliyun.com/product/arms
- 开发者工具: Chrome DevTools, React DevTools、岳鹰云真机https://yueying.effirst.com/index
- 连接真机: 对接Android设备, Lighthouse连接到真机设备
- 支持阿里系应用: 覆盖阿里系大部分应用, Lighthouse测试应用内的页面
- 分析能力可扩展: 自定义的分析能力沉淀到检测项目市场, 供业务同学配置使用
- 分析方案可组装: 基于检测项市场,业务同学可以通过组装audits来形成新的分析方案
- U4 内核私有指标 T2、内置首屏性能优化方案
LAAS:
Lighthouse as a Service
APP Mode: 移动端的页面, 阿里系应用(U4 应用)、Android Chrome
Headless Mode: 传统PC页面、H5页面
云测平台: 提供海量真机
关注我
前端搞起来[1]微信搜索公众号:xiaoyuanlianer666
公众号:小圆脸儿(xiaoyuanlianer666)、掘金:小圆脸儿。
「点赞、在看、分享」是对作者最大的支持❤️