web 开发中前端性能优化有哪些策略?
在当今数字化时代,网站的加载速度和响应能力对于用户体验至关重要。前端性能优化不仅仅是提高页面加载速度,更是提升用户满意度、增加用户留存率以及推动业务增长的关键因素。
当一个网站加载缓慢时,用户往往会感到沮丧并选择离开,导致潜在客户流失和品牌声誉受损。研究表明,页面加载时间每延迟一秒,转化率可能下降多达7%。因此,前端性能优化已成为Web开发中不可忽视的核心环节,通过优化资源加载、减少渲染时间和提高交互速度,可以显著提升网站的整体性能,为用户提供流畅而高效的浏览体验。
首先我们看一下 web 开发的架构是什么样子的。 如下图所示:
从上图中我们可以看到一个 web 开发会有很多层,从上层开始到底层:
web 前端 》服务器 》后端应用 》 基础服务 》数据库(广义)》运行环境(机器性能、网络带宽、等)
我们先看最上层:web 前端
广义的 web 前端,不仅仅包括 web 网页(官网、管理后台等),还包括 h5、小程序、APP 等。这些应用程序每天都会被成千上万或者更多的用户访问。这些应用的展现逻辑和响应速度自然会影响一个用户的浏览效果和体验,从而影响一个用户对一个产品的满意度和留存率。
个人认为影响一个用户体验最直接的两个因素是:
- 1、页面的美观度
- 2、页面的响应速度
今天我们不谈页面的美观度。我们着重谈谈提高一个页面的响应速度的策略有哪些手段。
前端性能优化策略
Web 开发中的前端性能优化是一个持续的过程,涉及到多个方面。下面是一些常见的前端性能优化策略:
减少HTTP请求:
上面描述了 http 的请求和响应过程:
页面上的每一个资源都是需要通过 http 请求获得。一次 http 请求过程是一个复杂的过程,如果我们有很多资源,那么就需要进行很多次的 http 请求。如下图所示:
css 和 JavaScript 是每个网页必须要使用到的资源,减少这两种资源的 http 请求次数可以显著减少页面加载时对服务器的请求次数,从而加快页面加载速度。一下是两种策略:
- 合并
CSS
和JavaScript
文件。 - 使用
CSS Sprites (雪碧图)
来减少图片请求。(CSS Sprites (雪碧图)
是一种将多个小的图像合并到一张较大的图像中的技术)
我们可以使用下面的工具来合并 css
和 JavaScript
:
- Webpack:强大的前端模块打包器,可以方便地进行资源的合并与处理。
- Parcel:一款轻量级的打包工具,也能实现资源的合并等功能。
- Gulp:通过配置任务可以实现 CSS 和 JavaScript 文件的合并等操作。
目前使用最多的就是 webpack
推荐使用。
浏览器缓存:
在Web开发中,HTTP 缓存头用于控制浏览器和其他中介缓存(如CDN、代理服务器等)如何存储和重用响应资源。合理使用缓存头可以显著提高网站性能,减少服务器负载并改善用户体验。
1. Cache-Control
Cache-Control
是一个主要的HTTP缓存头,用于指定请求和响应的缓存策略。常用的指令有:
public
:响应可以被任何缓存存储,包括浏览器、CDN等。private
:响应只能被浏览器缓存,不能被共享缓存存储。no-cache
:必须重新验证缓存数据的有效性,即每次都向服务器发送请求。no-store
:不缓存任何内容,避免敏感数据存储在缓存中。max-age=<seconds>
:指定响应可以被缓存的最大时间(秒)。s-maxage=<seconds>
:专用于共享缓存(如CDN),覆盖max-age
。
下面是一些示例:
Cache-Control: public, max-age=3600
2. Expires
Expires
头用于指定响应过期的具体日期和时间。它是Cache-Control: max-age
的替代方案,通常不推荐与max-age
一起使用。
示例:
Expires: Wed, 21 Oct 2024 07:28:00 GMT
3. ETag
ETag
(实体标签)是资源内容的唯一标识符。当浏览器缓存资源后,它会发送带有If-None-Match
头的请求,服务器会根据ETag
值判断资源是否改变。如果未改变,返回 304 Not Modified,减少传输的数据量。
示例:
ETag: "5d8c72a5edda3"
4. Last-Modified
Last-Modified
头指示资源最后修改的时间。浏览器会在后续请求中包含If-Modified-Since
头,服务器根据该时间判断资源是否改变,未改变返回304 Not Modified
。
示例:
Last-Modified: Tue, 20 May 2024 07:28:00 GMT
5. Pragma
Pragma
头是HTTP/1.0中的缓存控制指令,主要用于向后兼容。最常见的值是no-cache
。
示例:
Pragma: no-cache
6. Vary
Vary
头告诉缓存服务器在存储资源时要根据指定的请求头区分不同的版本。例如,根据Accept-Encoding
区分压缩和非压缩版本。
示例:
Vary: Accept-Encoding
使用示例
综合使用上述缓存头,可以实现精细的缓存控制。例如:
Cache-Control: public, max-age=3600
Expires: Wed, 21 Oct 2024 07:28:00 GMT
ETag: "5d8c72a5edda3"
Last-Modified: Tue, 20 May 2024 07:28:00 GMT
Vary: Accept-Encoding
在实际开发中,应根据资源的特点和应用需求合理设置缓存头,以最大化性能和用户体验。例如,静态资源(如 image
、CSS
、JS
文件)通常可以设置较长的缓存时间,而动态内容则需要更加严格的缓存控制策略。
更多内容可以查看我先前的文章: 跟我一起探索 HTTP-HTTP缓存 - 掘金 (juejin.cn)
压缩资源文件:
压缩资源也就是将一些大的资源文件(css
、JavaScript
、 html
等)的文件,在服务器响应的时候进行压缩,这样压缩后的文件就比较少,意味着需要更小的带宽,也就是在相同带宽的情况下可以响应更多的资源。
目前使用较多的服务器有 ngxin、apache、IIS等。下面是 nginx
配置 gzip
的例子:
http {
# 开启 gzip 压缩
gzip on;
# 压缩级别,1(最快)到 9(最小压缩)
gzip_comp_level 5;
# 压缩的最小长度,小于该值的文件将不会被压缩
gzip_min_length 256;
# 压缩的文件类型,可以根据需要添加或删除
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
# 是否在响应头中删除 Vary HTTP 响应头
gzip_vary on;
# 是否预加载压缩的数据
gzip_proxied any;
# 使用 deflate 压缩算法
gzip_http_version 1.1;
# 压缩文件扩展名
gzip_static on;
# 压缩后文件的缓存时间
gzip_disable "MSIE [1-6].";
}
server {
listen 80;
server_name example.com;
# 其他配置...
}
优化网页图片:
在我们的潜意识里面,远大的资源那么就需要更长的时间,比如我们下载一个10G影片,理论上来说就需要比下载一个 2G 的影片需要更长的时间。图片也不例外,在不影响用户视觉效果的情况下使用更小的图片可以提升网页展示的效果。
减小网页图片的策略有两种:
- 使用更小的图片格式,如WebP。
- 根据显示需求调整图片大小。
调整网页图片大小很容易理解,这里不多说。下面说一下 webp
:
WebP 是一种由 Google 开发的现代图像格式,旨在提供比传统 JPEG、PNG 和 GIF 格式更优的图像压缩。WebP 格式支持无损透明度(alpha transparency),并且可以包含动画,这使得它成为网页图像和图形的多功能选择。
它有下面的一些特点:
高效的压缩、无损透明度、 支持动画、 无损颜色、 支持元数据、 易于转换
Nginx 例子:
location /images/ {
# 检测客户端是否支持webp格式
set $webp_suffix "";
if ($http_accept ~* "webp") {
set $webp_suffix ".webp";
}
# 尝试提供webp格式图片,如果不存在则提供原始图片
try_files $uri$webp_suffix $uri =404;
}
配置好 nginx
前,我们需要提前将网页的图片资源转换为对应的 webp
资源。可以使用 Webpack
工具在构建的时候将对应的资源转换为 webp
格式。具体工具使用我已经帮大家找好了,可以作为参考:webpack下图片文件转webp方案 - 掘金 (juejin.cn)。
延迟加载(Lazy Loading) :
Lazy Loading
(延迟加载)是一种优化技术,用于提高网页性能和用户体验。
它的核心思想是延迟加载那些不是立即需要的资源,直到用户真正需要它们时才进行加载。这有助于减少初始页面加载时间,减少服务器的负载,从而提升性能。
延迟加载常用于下面的资源:
-
图片延迟加载:
当用户滚动页面时,只有当图片进入视口(viewport)时,才加载图片。对于长页面或包含大量图片的页面,这可以显著提高性能。
-
JavaScript
和CSS
文件延迟加载:延迟加载非关键的脚本和样式表,可以加快页面的初始加载时间。
-
Web 字体延迟加载:
延迟加载字体文件,直到它们需要显示时才加载。
-
数据延迟加载:
在需要时才从服务器加载数据,例如上拉分页加载。
Lazy Loading
的实现可以通过多种方式完成:
- 对于图片,我们使用原生浏览器
<img>
标签的loading="lazy"
属性可以自动实现图片的延迟加载。 - 对于
JavaScript
可以借助第三方库Lozad.js
、LazySizes
这样的库来实现延迟加载。 - 我们也可以编写自定义的
JavaScript
代码来监听滚动事件,并在资源需要时动态加载。
使用CDN(内容分发网络) :
CDN
(Content Delivery Network,内容分发网络)是一种分布式网络服务,通过将内容缓存到离用户更近的服务器上,来提高网站和应用程序的加载速度和性能。CDN
通过减少数据传输的地理距离来加速内容的分发,从而减少延迟和提高访问速度。
从图中我们可以看到,CDN服务在用户与服务器的中间,如果在 CDN 中返回用户需要的资源,那么这个请求链路就会少很多。
CDN
的主要特点和优势包括:
- 减少延迟:通过将内容缓存到全球多个数据中心,
CDN
可以确保用户从最近的服务器获取数据,从而减少数据传输的时间。 - 提高可靠性:
CDN
通过多个备份服务器提供内容,即使某个服务器或数据中心出现问题,用户仍然可以从其他服务器获取内容。 - 提升性能:
CDN
通常提供优化的路由和传输协议,以确保数据以最快速度传输。 - 内容缓存:
CDN
可以缓存静态内容,如图片
、视频
、CSS
和JavaScript
文件,以及动态内容,减少源服务器的负载。 - 负载均衡:
CDN
可以自动分配用户的请求到距离用户最近的服务器,以快速响应用户请求,提高效率。
从上面 CDN 的这些特点,我们不难发现,CDN 就是为了能提升资源的快速响应而设计的。所以一般情况下我们都会将静态资源放到 CDN 服务中。
使用预加载(Preloading)、预连接(Preconnect):
预加载 Preloading
预加载是指提前加载那些未来页面中会用到的资源,如脚本、样式表或图片等。这可以通过在 HTML 文档的 <head>
部分使用 <link>
标签的 rel="preload"
属性来实现。
预加载的主要目的:
- 减少页面加载时间:通过提前加载关键资源,可以减少页面完全加载所需的时间。
- 提高页面响应速度:确保关键资源在需要时已经可用,从而提高页面的响应速度。
示例:
<link rel="preload" href="style.css" as="style">
<link rel="preload" href="script.js" as="script">
预连接 Preconnect
预连接是一种更轻量级的资源优化技术,它允许浏览器提前与服务器建立连接,以便更快地加载资源。这通常用于那些需要通过 HTTPS 加密连接加载资源的情况。
预连接的主要目的:
- 减少连接建立时间:通过提前建立连接,可以减少后续资源加载时的连接建立时间。
- 优化 HTTPS 资源加载:对于需要通过 HTTPS 加密连接加载的资源,预连接可以显著提高加载速度。
<link rel="preconnect" href="https://example.com">
优化CSS选择器:
使用复杂的CSS选择器会导致浏览器花费更多时间来匹配元素。所以我们写 css 的选择器要简单化。
以下是一些优化 CSS 选择器的技巧,供大家参考:
-
避免使用通用选择器:
通用选择器
*
会匹配页面上的所有元素,这可能会导致性能问题。尽量避免使用它们,除非绝对必要。 -
减少选择器的复杂性:
复杂的选择器会增加浏览器的计算负担。尽量使用类选择器和
ID
选择器,因为它们唯一所以它们更快。 -
使用类选择器:
类选择器一种较快的选择器,因为它们可以直接通过元素的类属性访问,推荐使用。
-
推荐 ID 选择器的使用:
ID 选择器很快,因为每个页面上 ID 是唯一的。
-
避免使用属性选择器:
属性选择器会很慢,因为它们需要浏览器检查每个元素的属性。
-
编写高效的 CSS:
避免写重复的样式,将重复的样式合并以公共使用。
-
利用浏览器的开发者工具:
使用浏览器的开发者工具(如 Chrome DevTools)来分析和识别性能瓶颈。
异步加载JavaScript:
常用的异步加载 JavaScript 的方式有下面几种:
-
使用
async
属性在
<script>
标签中使用async
属性可以异步加载脚本,而不会阻塞页面的解析。脚本将在下载后尽快执行。 -
使用
defer
属性defer
属性会延迟脚本的执行直到文档解析完成。这对于依赖于 DOM 元素的脚本非常有用。 -
动态创建
<script>
标签使用 JavaScript 动态创建
<script>
标签并设置src
属性,可以实现脚本的异步加载。
使用工具分析性能:
目前作为 web 开发的我们,主要使用的调试工具就是浏览器自带的 开发者工具
,用好这个工具其实我们已经开始了性能优化的第一部,那么如何使用开发者工具进行性能优化我给大家找到一篇文章作为参考:
前端性能优化之利用 Chrome Dev Tools 进行页面性能分析 - 知乎 (zhihu.com)
优化DOM结构:
下面是一些优化DOM结构的常用方法:
- 减少节点数量:移除不必要的元素和嵌套,精简DOM树,降低浏览器的渲染负担。。
- 批量操作:合并多次操作,减少页面重绘和重排次数。
- 延迟加载:通过懒加载、异步加载非关键资源,减少初始页面加载时的DOM元素数量和复杂度,提升页面加载速度。
缓存的重要意义
在 web 开发中,缓存具有极其重要的意义。缓存不仅仅是指前端缓存或者服务端缓存又或者是数据库缓存等。缓存是一种策略,在 web 开发中处处得以体现。上面的内容谈了前端缓存,那还有很多缓存(CDN 缓存,redis 缓存、内存缓存,双 cache缓存,一级、二级缓存)技术。不过目的只有一个就是提升用户体验:
缓存的好处主要体现在以下几个方面:
提升性能:
避免重复请求资源,减少数据传输量和服务器负载,从而显著提高页面加载速度,为用户提供更流畅的体验。例如,已经缓存的静态文件无需再次从服务器获取。
增强可用性:
在网络状况不佳或服务器出现短暂故障时,缓存的内容可以继续提供服务,一定程度上保证了网站或应用的可用性。
节省带宽:
大量重复请求被缓存处理,减少了对网络带宽的占用,从而在有限的带宽下可以加载更多远程资源。
改善用户体验:
快速的响应速度能让用户更满意,增加用户对产品的粘性和忠诚度。
减轻服务器压力:
不用频繁处理相同的请求,让服务器可以更专注于处理新的和复杂的业务逻辑,提升服务器性能。
总结:
上面主要总结了前端性能优化的几种常见策略:可以分为几种
- 从代码层面编写优化
- 从浏览器加载策略优化
- 从服务器配置优化
- 资源压缩优化
- 使用 CDN 缓存策略
这些策略可以根据项目的具体需求和目标进行调整和实施。性能优化是一个持续的过程,需要定期审查和更新优化策略。
没有一层不变的方案和技术,需要根据自己的实际情况来使用适合的技术。
这些策略和方案同样也不是一个人或者某几个人的任务,是整个开发团队需要共同面对的主题和任务。