前端性能优化:从系统分析讲到实践策略

前言

在过去几年,我曾经写过几篇和性能优化相关的文章,例如有性能优化方法相关的,有性能监控相关的。但是都只关注于局部,没有从整体上去看待、分析性能优化。所以本文打算尝试从整体上去分析前端性能优化,从性能指标,性能分析,到性能优化这一整条链路来分析前端性能优化。

在做性能优化之前,首先要了解如何评价性能好坏,不能只凭主观感受来判断。因为每个人的感受都不一样,例如首屏加载时间,有人觉得 2 秒就够快了,有人觉得 5 秒也不错。为了避免这种情况,我们要建立一个性能评价系统,在统一的标准下,分析页面性能好坏。这可以通过一系列性能指标来实现,例如 FCP、LCP、CLS 等性能指标。

对性能指标有一定了解后,就可以开始编写 SDK 来收集性能数据了,当然,也可以使用开源的监控系统。一般的监控系统都会有一个监测平台用来查看性能数据。然后我们就可以分析出来页面的性能瓶颈在哪里,再使用对应的性能优化方法来解决性能问题。

以上,是关于如何做性能优化的方法论,下面来看一下具体如何做。

性能指标

在优化性能的过程中,我们需要依赖一些具体的性能指标来对页面性能进行分析。然后通过对比优化前后的性能指标,我们可以准确判断性能优化的效果。对于性能指标,我们一般关注以下 9 个就可以了。

在这里插入图片描述

FP

FP(first-paint),从页面加载开始到第一个像素绘制到屏幕上的时间。其实把 FP 理解成白屏时间也是没问题的。

FCP

FCP(first-contentful-paint),从页面加载开始到页面内容的任何部分在屏幕上完成渲染的时间。对于该指标,"内容"指的是文本、图像(包括背景图像)、<svg> 元素或非白色的 <canvas> 元素。

在这里插入图片描述

为了提供良好的用户体验,FCP 的分数应该控制在 1.8 秒以内。

在这里插入图片描述

LCP

LCP(largest-contentful-paint),从页面加载开始到最大文本块或图像元素在屏幕上完成渲染的时间。LCP 指标会根据页面首次开始加载的时间点来报告可视区域内可见的最大图像或文本块完成渲染的相对时间。

一个良好的 LCP 分数应该控制在 2.5 秒以内。

在这里插入图片描述

FCP 和 LCP 的区别是:FCP 只要任意内容绘制完成就触发,LCP 是最大内容渲染完成时触发。

在这里插入图片描述

LCP 考察的元素类型为:

  • <img>元素
  • 内嵌在<svg>元素内的<image>元素
  • <video>元素(使用封面图像)
  • 通过url())函数(而非使用CSS 渐变)加载的带有背景图像的元素
  • 包含文本节点或其他行内级文本元素子元素的块级元素。

    CLS

    CLS(layout-shift),从页面加载开始和其生命周期状态变为隐藏期间发生的所有意外布局偏移的累积分数。

布局偏移分数的计算方式如下:

布局偏移分数 = 影响分数 * 距离分数

影响分数测量不稳定元素对两帧之间的可视区域产生的影响。

距离分数指的是任何不稳定元素在一帧中位移的最大距离(水平或垂直)除以可视区域的最大尺寸维度(宽度或高度,以较大者为准)。

CLS 就是把所有布局偏移分数加起来的总和

当一个 DOM 在两个渲染帧之间产生了位移,就会触发 CLS(如图所示)。

在这里插入图片描述

在这里插入图片描述

上图中的矩形从左上角移动到了右边,这就算是一次布局偏移。同时,在 CLS 中,有一个叫会话窗口的术语:一个或多个快速连续发生的单次布局偏移,每次偏移相隔的时间少于 1 秒,且整个窗口的最大持续时长为 5 秒。

image.png

例如上图中的第二个会话窗口,它里面有四次布局偏移,每一次偏移之间的间隔必须少于 1 秒,并且第一个偏移和最后一个偏移之间的时间不能超过 5 秒,这样才能算是一次会话窗口。如果不符合这个条件,就算是一个新的会话窗口。可能有人会问,为什么要这样规定?其实这是 chrome 团队根据大量的实验和研究得出的分析结果 Evolving the CLS metric。

CLS 一共有三种计算方式:

  1. 累加
  2. 取所有会话窗口的平均数
  3. 取所有会话窗口中的最大值

第三种方式是目前最优的计算方式,每次只取所有会话窗口的最大值,用来反映页面布局偏移的最差情况。详情请看 Evolving the CLS metric。

首屏渲染时间

页面从加载开始到页面渲染完毕的时间,如果有些页面的内容特别多,我们可以只取当前屏幕展示的内容的加载时间作为首屏渲染时间。因为其他部分对于用户来说看不到,所以可以忽略那部分的加载时间。

FPS 帧率

页面的帧率就是每秒显示多少帧,一般使用 requestAnimationFrame() 来计算当前页面的 FPS。

资源加载时间

资源加载时间指的是每个静态资源文件的加载时间,通过它可以找到哪些资源加载慢,然后再分析原因。

缓存命中率

缓存命中率是用来分析你的缓存策略、内容分包机制做得好不好。一般缓存命中率越高,说明你缓存策略、内容分包机制做得越好。

接口请求耗时

通过接口请求耗时,可以分析哪些接口慢,然后再找出是服务器问题或者是接口内容过大或是网络问题。

小结

通过这 9 个指标,我们就可以对页面做到一个全面的、客观的分析,进而知道性能瓶颈出在哪。例如资源加载时间比较慢,我们可以分析出来有可能是网络问题,再进而分析是不是 DNS 有问题、或者网络带宽问题、还是 CDN 有问题等等;也可以看看是不是资源体积过大,需要进行压缩或者拆包。

总的来说,了解性能指标可以提升你对性能优化的认识,帮助你更好的实现性能优化。

性能监控

通过监控,我们可以拿到所需的性能指标数据。如果开发监控系统的压力过大,我们可以使用开源的监控系统,例如 sentry 就是一个很不错的监控平台,除了能收集性能数据,还能收集错误数据。不过如果不想使用这种大而全的监控平台,也可以选择自己开发一个监控平台,一个完整的监控平台包含了监控 SDK、数据清洗和存储、数据展示。两三个人花一两个月的时间就能开发出来一个够用的监控系统了,如果对监控 SDK 有兴趣的可以看看我这篇文章 前端监控 SDK 的一些技术要点原理分析。

如果不需要持续的监控页面性能,而只是想进行一次评测,那我们可以使用 chrome 浏览器自带的 lighthouse 工具来进行测试。

在这里插入图片描述

选择你想要收集的数据和所在平台,然后点击 Analyze page load 即可开始测试。

在这里插入图片描述

从上图可以看出,几个核心的性能指标都已经有了,并且还能看到整体页面和性能分数,非常的简单和直观。

小结

收集性能数据的方式基本上就是两种:

  • 使用监控 SDK(数据全面,有难度)
  • 使用测试工具(能收集基本的数据,使用简单)

通常情况下,测试工具就够用了,除非你的网站需要追求极致的性能和用户体验,否则不需要上监控平台。

性能优化方法

性能监控、性能指标是用来收集数据、分析性能瓶颈用的,在找到性能瓶颈后,就得使用对应的性能方法去优化它。我觉得要做性能优化,可以从两方面入手:

  • 加载时(优化页面加载速度)
  • 运行时(资源加载完毕后,在页面开始运行的时候进行优化)

所有的性能优化手段都可以归纳为以上两类。其中对性能影响最大、收益最大的是在页面加载时进行优化。

在这里插入图片描述


从上图可以看到,一共有 23 条性能优化方法,其中加载时优化有 10 条,运行时优化有 13 条。由于篇幅有限,这 23 条性能优化方法不能逐一展开细讲,就讲讲其中的一条吧 使用 CDN

静态资源使用 CDN

内容分发网络(CDN)是一组分布在多个不同地理位置的 Web 服务器。我们都知道,当服务器离用户越远时,延迟越高。CDN 就是为了解决这一问题,在多个位置部署服务器,让用户离服务器更近,从而缩短请求时间。

CDN 原理

当用户访问一个网站时,如果没有 CDN,过程是这样的:

  1. 浏览器要将域名解析为 IP 地址,所以需要向本地 DNS 发出请求。
  2. 本地 DNS 依次向根服务器、顶级域名服务器、权限服务器发出请求,得到网站服务器的 IP 地址。
  3. 本地 DNS 将 IP 地址发回给浏览器,浏览器向网站服务器 IP 地址发出请求并得到资源。

如果用户访问的网站部署了 CDN,过程是这样的:

  1. 浏览器要将域名解析为 IP 地址,所以需要向本地 DNS 发出请求。
  2. 本地 DNS 依次向根服务器、顶级域名服务器、权限服务器发出请求,得到全局负载均衡系统(GSLB)的 IP 地址。
  3. 本地 DNS 再向 GSLB 发出请求,GSLB 的主要功能是根据本地 DNS 的 IP 地址判断用户的位置,筛选出距离用户较近的本地负载均衡系统(SLB),并将该 SLB 的 IP 地址作为结果返回给本地 DNS。
  4. 本地 DNS 将 SLB 的 IP 地址发回给浏览器,浏览器向 SLB 发出请求。
  5. SLB 根据浏览器请求的资源和地址,选出最优的缓存服务器发回给浏览器。
  6. 浏览器再根据 SLB 发回的地址重定向到缓存服务器。
  7. 如果缓存服务器有浏览器需要的资源,就将资源发回给浏览器。如果没有,就向源服务器请求资源,再发给浏览器并缓存在本地。

小结

由于篇幅有限,文中提及的一共 23 条性能优化方法可以看我的这篇文章前端性能优化 24 条建议(2020)。

性能优化实战

在所有性能优化方法中,收益最大的有三个:压缩、懒加载和分包、缓存策略。绝大多数的应用做了这三个优化后,页面性能都能得到极大的提升。

压缩

压缩包含代码压缩和文件压缩,常用的代码压缩工具有 terser、uglify,文件压缩一般使用 gzip。1m 的文件使用 gzip 压缩后体积一般是 300-400kb。

压缩通常情况都是在前端打包的时候做,然后将打包好的文件上传到服务器。用户访问网站时,nginx 先查看用户访问的资源是否有 .gz 后缀的文件(gzip 压缩后的文件),如果有就返回,没有就自己压缩后再返回。这一块需要在 nginx 上进行配置才支持,默认不开启。尽量不要让 nginx 压缩,因为访问量高的时候,对服务器压力很大。

压缩是对整体项目的体积大小进行优化。

懒加载和分包

路由懒加载基本上每个前端都会,但是某些系统仍然需要手动优化。比如一些构建工具会默认把所有的第三方包都打在 verdor.js 文件里。但实际上并不是所有的页面都需要把所有的第三方包都加载进来,所以需要做好具体的分包策略来进行优化。分包主要是为了对打包好的资源进行拆分,让一些不是所有页面都要使用的资源做到懒加载。

懒加载和分包不改变项目的体积大小,而是将一些不是那么重要的资源放到后面再加载。比如有 10 个页面的资源,每个 1m,假设加载时间为 1 秒,都做成懒加载后,每次访问页面只需要一秒,而不是都打包在一起,首次访问要 10 秒。

缓存策略

浏览器缓存分为强制缓存协商缓存,强制缓存就是缓存生效期间,浏览器会直接使用缓存的资源,而不会发起请求。协商缓存就是浏览器发起请求后,发现文件未变化,然后再使用缓存。

缓存策略就是围绕着强制缓存、协商缓存做的优化方案。在做优化之前,还要给大家再介绍一下缓存命中率。

缓存命中率=缓存命中的资源/所有请求的资源。缓存命中率越高,说明你的缓存策略做得越好。

缓存策略一般通过 nginx 来实现,当然也可以自己手写代码实现。现在常用的缓存策略是:

  1. 打包文件时所有文件名称按照文件内容进行 hash 命名,当内容改变时文件名也会改变
  2. 在 nginx 配置 html 文件为 cache-control: no-cache,每次浏览器请求 html 时都要向服务器发起请求,查询 html 文件是否有变化,即协商缓存
  3. 其他所有静态资源文件设为长缓存(例如缓存时间为一年),即强制缓存

这样优化后,除了首次打开页面需要加载静态资源,后面再打开页面就不需要了,所以能在瞬间打开页面。当项目代码更新后,相关的 js、css 资源文件会发生变化,其他不变的部分,代码不会变化。当资源文件发生变化后,html 也会发生变化(引用的资源文件名称变了)。然后浏览器会重新加载 html 中发生变更的资源,没有变化的资源会使用缓存。目前只有 webpack 支持根据文件内容生成 hash,所以说 vite 是代替不了 webpack 的,它们会同时存在。

想了解如何利用 webpack 实现文件精确缓存的,可以看我这篇文章:webpack + express 实现文件精确缓存

现在我们再看一下其他比较知名的网站是怎么做缓存的。

一些比较好例子:

  • https://www.zhihu.com/
  • https://bbs.hupu.com/all-nba
  • https://www.google.com/

不好的例子:

  • 省略...

为了避免不必要的问题,不好的示例还是不帖了,但是大家可以找一些小网站或者小公司的官网看看,基本上都没怎么做性能优化。

另外,有些细心的同学可能会发现即使没有设置 cache-control,浏览器也会进行缓存。这是因为没有设置 cache-control 响应头时,浏览器将自行选择缓存策略。

浏览器默认行为:即使响应头部中没有设置 Cache-Control 和 Expires,浏览器仍然会缓存某些资源,这是浏览器的默认行为,是为了提升性能进行的优化,每个浏览器的行为可能不一致,有些浏览器甚至没有这样的优化。

性能优化的成本和收益

在绝大多数的单页面应用(SPA)中,页面采用懒加载的策略,并且通过打包工具实现代码压缩,基本上不会有性能问题。但是随着项目规模的扩大,可能会出现性能问题,这时就需要做性能优化了。

但是,性能优化并非无止境的进行。我们需要充分考虑优化的成本和收益,而不是盲目地采用所有可能的优化方法,因为这将带来过高的开发和维护成本。只有当项目的性能表现无法满足预设的性能指标时,我们才需要进行进一步的优化。这样,我们可以在保证项目性能的同时,也能控制好优化的成本。

举例,性能优化实战中说到 vite 不能实现按内容进行 hash。也可以打包时用 webpack 来替代 vite。但成本比较高,没什么必要,除非对性能有极端的要求。

疑难解答

不好的代码是否会影响性能优化

如果做好了项目的整体架构设计(结合性能优化方法论、前端工程化、合理的研发流程、代码规范和 CodeReview),不好的代码对性能影响很小,除非写一些死循环代码。当然,前端工程化这些概念非常宽泛也不好理解,有兴趣可以看我写的这一本电子书:带你入门前端工程

举例,一个页面几千行代码会不会影响页面性能。如果这个页面确实需要这些代码,那说明代码总量不会变,再怎么拆分还是那么多代码。所有代码写在一个页面只会影响代码可读性,而不会影响性能。

总结

关于性能优化的内容已经说完了,但是我还想说点别的,主要想说一下我们应该学习哪些技术才能让它更加保值。

在我看来,越偏向于业务的技术越不容易过时,为什么呢?需求在变,技术一直在变,业务也一直在迭代。前端技术的发展非常快,也涌现出很多的框架(例如 HTML4 到 HTML5 的升级,或者从jQuery 到前端三大框架的转变),但是总归就是两个字:效率。

作为开发者,我们需要保持好奇心和学习热情,不断探索新的技术,只有这样,我们才能在这个快速发展的时代中立于不败之地。介绍一款程序员都应该知道的软件JNPF快速开发平台,很多人都尝试用过它,它是功能的集大成者,任何信息化系统都可以基于它开发出来。

JNPF可以实现应用从创建、配置、开发、测试到发布、运维、升级等完整生命周期的管理。减少了传统应用程序的代码编写量,通过图形化、可视化的界面,以拖放组件的方式,即可快速生成应用程序的产品,大幅降低了开发企业管理类软件的难度。

当然,我更建议大家成为一个全栈,不要把自己的定位局限于前端。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/839174.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用 RisingWave 和 Redash 处理和可视化实时数据

在创建流处理管道时&#xff0c;需要两个关键组件&#xff1a;一个用于处理和转换数据&#xff0c;一个用于数据可视化。RisingWave 和 Redash 就提供了一个优秀的解决方案。 RisingWave 是一个支持实时数据处理的分布式 SQL 流数据库。它提供增量更新的物化视图&#xff0c;使…

TRICONEX 3720 技术特点

TRICONEX 3720是一款安全管理系统通讯模块&#xff0c;通常用于工业自动化和安全系统中。它的主要作用是在控制系统中处理和管理数据通讯&#xff0c;确保信息在各个组件之间的有效传递。以下是关于TRICONEX 3720的详细介绍&#xff1a; 功能定位&#xff1a;TRICONEX 3720作为…

计算机操作系统总结(1)

1操作系统的概念&#xff08;定义&#xff09;功能和目标 (1)什么是操作系统&#xff1f; &#xff08;2&#xff09;操作系统的功能和目标—作为系统资源的管理者 &#xff08;3&#xff09;操作系统的功能和目标—向上层提供方便易用的服务 &#xff08;4&#xff09;操作系…

IP学习——ospf1

OSPF:开放式最短路径优先协议 无类别IGP协议&#xff1a;链路状态型。基于 LSA收敛&#xff0c;故更新量较大&#xff0c;为在中大型网络正常工作&#xff0c;需要进行结构化的部署---区域划分、ip地址规划 支持等开销负载均衡 组播更新 ---224.0.0.5 224.0.0.6 …

区块链开发:区块链软件开发包装相关解析

区块链开发是指设计、构建和维护基于区块链技术的应用程序或系统的过程。区块链是一种分布式账本技术&#xff0c;它通过去中心化的方式记录和验证数据&#xff0c;确保数据的透明性、不可篡改性和安全性。区块链开发者使用各种编程语言和框架来创建这些应用程序。 在加密货币领…

【Linux】-Linux文件的上传和下载、压缩和解压[9]

目录 前言 一、上传和下载 1、使用finalshell对Linux系统进行上传下载 2、rz、sz命令 二、解压和压缩 1、压缩格式 2、tar命令压缩 3、tar命令压缩 4、zip命令压缩文件 5、unzip命令解压文件 前言 在Linux系统中&#xff0c;文件的上传和下载、压缩和解压是非常重要…

暴风雨的短视频:成都鼎茂宏升文化传媒公司

暴风雨的短视频&#xff1a;大自然的力与美 ​随着科技的进步和网络的普及&#xff0c;短视频已经成为我们生活中不可或缺的一部分。在这些短暂而精彩的瞬间里&#xff0c;我们得以窥见世界的每一个角落&#xff0c;感受生活的多样性和复杂性。成都鼎茂宏升文化传媒公司而当我…

go语言之基本数据类型

文章目录 基础数据类型分类整数类型有符号整数无符号整数默认整数类型 浮点类型复数类型布尔类型字符类型字符串replace字符串获取字符串长度字符串的拼接字符串获取指定位置字符更多string操作 数据类型之间的转换其它基本类型转字符串类型fmt包中的Sprintfstrconv包中函数 字…

java 解决异常 Class path contains multiple SLF4J bindings

前面 为了使用 j2cache pom中导入了 <dependency><groupId>net.oschina.j2cache</groupId><artifactId>j2cache-core</artifactId><version>2.8.4-release</version> </dependency><dependency><groupId>net.osc…

Java开发之JDBC

JDBC 介绍JDBC程序&#xff08;Statement&#xff09;相关细节URLResultSet 连接池程序&#xff08;PreparedStatement&#xff09; 本文主要记录一下学习JDBC的一些知识点 介绍JDBC 首先谈谈什么是JDBC。下面放几张图&#xff0c;大致就可以清楚JDBC了。程序&#xff08;Sta…

网络编程—— Http的Get请求

http: hyper text transport protocal:超文本传输协议。 http是一种基于客户端-服务器模式的协议(Client-Server)。它规定只能由客户端先发起请求给服务器&#xff0c; 服务器做出响应。 http数据传输以数据报文的形式进行&#xff0c; 客户端向服务器发起的请求叫做请求报文。…

【Linux玩物志】Linux环境开发基本工具使用(终章) ——git与gdb调试器

W...Y的主页 &#x1f60a; 代码仓库分享 &#x1f495; 前言&#xff1a;这是最后一篇Linux工具篇&#xff0c;今天这篇文章我们要来简单讲一件git与调试器gdb。 目录 使用 git 命令行 安装 git 在 Github 创建项目 注册账号 创建项目 ​编辑 下载项目到本地 在 Gite…

融合基因组序列识别scATAC-seq的细胞类型

利用scATAC-seq技术进行单细胞分析&#xff0c;可以在单细胞分辨率下深入了解基因调控和表观遗传异质性&#xff0c;但由于数据的高维性和极端稀疏性&#xff0c;scATAC-seq的细胞注释仍然具有挑战性。现有的细胞注释方法大多集中在细胞峰矩阵上&#xff0c;没有充分利用潜在的…

商品指数创年内新高,粘性通胀成为美联储噩梦

文章概述 虽然美国4月CPI增幅放缓让美联储今年降息的可能性大增&#xff0c;但与此同时&#xff0c;大宗商品价格却达到了一年来的最高水平&#xff0c;粘性通胀可能成为美联储的噩梦。数据显示&#xff0c;跟踪24种能源、金属和农业合约彭博大宗商品现货指数今年以来已经上涨…

使用Flask ORM进行数据库操作的技术指南

文章目录 安装Flask SQLAlchemy配置数据库连接创建模型类数据库操作插入数据查询数据更新数据删除数据 总结 Flask是一个轻量级的Python Web框架&#xff0c;其灵活性和易用性使其成为开发人员喜爱的选择。而ORM&#xff08;对象关系映射&#xff09;则是一种将数据库中的表与面…

LeetCode题练习与总结:二叉树的最大深度--104

一、题目描述 给定一个二叉树 root &#xff0c;返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;3示例 2&#xff1a; 输入&#xff1a;root […

ESP32开发环境搭建Windows VSCode集成Espressif IDF插件开发环境搭建 IDF_V5.2.1

一、安装Visual Studio Code 下载地址&#xff1a;Download Visual Studio Code - Mac, Linux, Windows 打开上方链接&#xff0c;选择页面中的Windows版本&#xff0c;单击下载 将下载好的VSCodeUserSetup-x64-1.89.1.exe。单击右键&#xff0c;选择以管理员身份运行&#xf…

jenkins自动化部署详解

一、准备相关软件 整个自动化部署的过程就是从git仓库拉取最新代码&#xff0c;然后使用maven进行构建代码&#xff0c;构建包构建好了之后&#xff0c;通过ssh发送到发布服务的linux服务器的目录&#xff0c;最后在此服务器上执行相关的linux命令进行发布。 此篇文章jenkins…

【iOS安全】BurpSuite iOS https抓包 | DNS Spoofing

BurpSuite 完整版安装 参考&#xff1a;Admin Team 小数智 的博客 下载 Burp Suite 2021.6 官网下载地址 https://portswigger.net/burp/releases &#xff08;下载并安装好&#xff09; jdk 使用的是11.0.10 https://www.oracle.com/java/technologies/javase-jdk11-downl…

实用css整理

网页一键变灰 body{filter: grayscale(1); } 一般用于特殊时期&#xff0c;网页变灰&#xff0c;只需要给body标签添加这行样式代码。 一键换主题色 body {filter: hue-rotate(45deg);} 给body标签设置这个属性样式&#xff0c;改变角度看看效果吧。 设置字母大小写 p {t…