一个快速、响应迅速且用户友好的网站不仅可以吸引和留住访问者,还有助于提高搜索引擎排名、提高转化率和改善用户体验 (UX)。
作为软件工程师或 Web 开发人员,必须在项目中优先考虑性能优化技术。
在本文中,我将分享通过各种方法来优化 JavaScript 代码,包括最小化文件大小、减少网络请求、利用缓存和异步加载,以及采用最佳实践来确保更快的加载时间和改进的用户体验。
1、最小化文件大小
影响网站加载时间的关键因素之一是提供给用户的文件大小。
较大的文件需要更多时间来下载,并可能导致你的网站加载缓慢,从而导致用户体验欠佳。JavaScript 文件也不例外,优化它们的大小是提高网站性能的基本步骤。
缩小是在不影响其功能的情况下删除不必要的字符(例如空格、注释和换行符)并缩短 JavaScript 代码中的变量名称的过程。这导致文件大小显着减小,进而导致更快的加载时间和更高的性能。
01)、JavaScript 代码示例:缩小前后
让我们看一个简单的例子来理解缩小对文件大小的影响:
缩小前:
// Function to calculate the sum of two numbers
function addNumbers(num1, num2) {
return num1 + num2;
}
// Use the function to calculate the sum of 3 and 5
const sum = addNumbers(3, 5);
// Log the result to the console
console.log("The sum is:", sum);
缩小后:
function addNumbers(n,e){return n+e}const sum=addNumbers(3,5);console.log("The sum is:",sum);
如你所见,代码的缩小版本明显更小,删除了不必要的字符并缩短了变量名。这会导致更小的文件大小和更快的加载时间,而不会影响代码的功能。
2、文件压缩
压缩是另一种用于减小文件大小的技术,可以缩短网站加载时间。
它的工作原理是应用算法来压缩文件中的数据,使文件更小而不失去其功能。当浏览器请求压缩文件时,它会即时解压缩,以便正确呈现和执行内容。
有两种广泛使用的 JavaScript 文件压缩算法:Gzip 和 Brotli。
Gzip 长期以来一直是事实上的标准,但是由 Google 开发的更新的压缩算法 Brotli 因其优越的压缩比和速度而变得越来越流行。
01)Gzip 和 Brotli 压缩方法
Gzip:Gzip 是一种广泛采用的压缩算法,可以显着减小 JavaScript 文件的大小。Gzip 使用 Deflate 算法,该算法结合了 LZ77 和霍夫曼编码以高效地压缩数据。
Brotli:Brotli 是 Google 开发的一种较新的压缩算法,提供比 Gzip 更好的压缩率。Brotli 结合使用 LZ77、霍夫曼编码和一种新颖的上下文建模技术来实现更高的压缩率。
在大多数情况下,Brotli 在压缩比和速度方面都优于 Gzip,这使其成为现代 Web 应用程序的一个有吸引力的选择。
02)压缩的服务器端配置
要提供压缩的 JavaScript 文件,你需要将服务器配置为使用 Gzip 或 Brotli 压缩文件,然后再将它们发送到客户端。
具体配置步骤因您的服务器类型(例如 Apache、Nginx 或 Node.js)而异。以下是如何在流行的服务器类型上启用压缩的简要概述:
- Apache:为 Gzip 压缩启用 mod_deflate 模块或为 Brotli 压缩启用 mod_brotli 模块,并在 .htaccess 文件或虚拟主机配置中配置适当的设置。
- Nginx:在 Nginx 配置文件中使用 gzip 或 brotli 指令启用压缩并指定设置。
- Node.js:对于基于 Node.js 的服务器,您可以将中间件(例如用于 Gzip 的压缩或用于 Brotli 的 shrink-ray-current)与 Express 或类似的 Web 框架结合使用。
请务必注意,某些浏览器可能不支持 Brotli 压缩,因此,最好将你的服务器配置为在不支持 Brotli 时回退到 Gzip。
这确保了所有浏览器的最佳兼容性和性能。
3、捆绑以减少网络请求
减少网络请求的数量对于提高网站性能至关重要,因为每个请求都会增加延迟并消耗带宽。
01)捆绑说明
捆绑是将多个 JavaScript 文件组合成一个文件的过程。这减少了浏览器需要发出的 HTTP 请求的数量,从而加快了加载过程。捆绑可以显着提高网站性能,尤其是对于具有大量较小 JavaScript 文件的网站。
02)捆绑工具
有几种流行的工具可用于捆绑 JavaScript 文件,每种工具都有其独特的特性和优势。
以下是一些广泛使用的捆绑工具:
- Webpack:Webpack 是一个功能强大且灵活的模块捆绑器,它不仅可以捆绑 JavaScript 文件,还可以处理样式表和图像等其他资产。它具有强大的插件生态系统,允许你根据需要扩展其功能。
- Rollup:Rollup 是另一个流行的 JavaScript 模块打包器,专注于简单性和性能。它特别适合捆绑库,可以输出多种格式,包括 CommonJS、AMD 和 ES 模块。
03)JavaScript 代码示例:捆绑多个文件
为了演示捆绑过程,我们假设你有三个独立的 JavaScript 文件:
// main.js
import { greet } from './greeting.js';
import { calculate } from './math.js';
console.log(greet('John'));
console.log(calculate(5, 3));
// greeting.js
export function greet(name) {
return `Hello, ${name}!`;
}
// math.js
export function calculate(x, y) {
return x * y;
}
使用 Webpack 或 Rollup 等捆绑工具,你可以将这些文件组合成一个捆绑文件。输出可能看起来像这样:
(function () {
'use strict';
function greet(name) {
return `Hello, ${name}!`;
}
function calculate(x, y) {
return x * y;
}
console.log(greet('John'));
console.log(calculate(5, 3));
})();
如你所见,捆绑文件将原始文件中的所有必要代码包含在一个独立的单元中,从而减少了加载脚本所需的网络请求数。
通过最小化请求数量,你可以减少浏览器下载和处理必要资源所需的时间,从而缩短加载时间并提供更灵敏的用户体验。
4、为图像和图标使用 Sprite
利用图像精灵是另一种减少网络请求和提高网站性能的技术。
精灵本质上是一个包含多个较小图像(例如图标或 UI 元素)的图像文件。
01)、图像精灵的解释
图像精灵是一个大图像,包含多个以网格状图案排列的小图像。在 CSS 或 JavaScript 代码中,可以通过指定图像的位置和尺寸来引用精灵中的各个图像。
此方法允许仅通过单个 HTTP 请求加载许多图像,从而减少延迟并缩短加载时间。
02)、创建图像精灵
要创建图像精灵,可以使用各种工具,例如:
- Sprite 生成器工具:SpritePad 或 Stitches 等在线工具允许您上传多张图像并自动生成一个 sprite,以及相应的 CSS 代码。
- 图像编辑软件:Adobe Photoshop 或 GIMP 等程序可用于通过在新文件中排列较小的图像并将结果导出为单个图像来手动创建精灵。
03)、CSS 代码示例:使用图像精灵
假设您有一个名为“icons.png”的精灵图像,其中包含多个图标,你可以使用以下 CSS 代码将各个图标显示为不同元素的背景图像:
.icon {
width: 32px;
height: 32px;
background-image: url('icons.png');
}
.icon-search {
background-position: 0 0;
}
.icon-settings {
background-position: -32px 0;
}
.icon-user {
background-position: -64px 0;
}
每个图标类指定相应图标在 sprite 中的位置,无需额外的 HTTP 请求即可显示所需的图像。
通过将这些较小的图像组合成一个文件,浏览器只需要请求一个图像,减少了 HTTP 请求的数量。
5、延迟加载资源
延迟加载是一种将非关键资源的加载推迟到实际需要时才加载的技术。
这意味着你无需预先加载所有资源,而只需加载即时视图所需的资源,而其余的则在它们变得相关时获取。延迟加载可以大大缩短网站的初始加载时间和感知性能,尤其是在处理图像或冗长脚本等大型资产时。
01)、JavaScript 代码示例:实现延迟加载
为了说明延迟加载,让我们使用仅当图像在视口中可见时才加载图像的示例。这可以使用 IntersectionObserver API 来实现。
这是一个简单的实现:
首先,向你的图像元素添加一个 data-src 属性,其中包含实际的图像源:
然后,创建一个脚本来设置 IntersectionObserver 以在图像进入视口时加载图像:
document.addEventListener('DOMContentLoaded', function () {
const lazyImages = [].slice.call(document.querySelectorAll('.lazy-load'));
if ('IntersectionObserver' in window) {
const lazyImageObserver = new IntersectionObserver(function (entries, observer) {
entries.forEach(function (entry) {
if (entry.isIntersecting) {
const lazyImage = entry.target;
lazyImage.src = lazyImage.dataset.src;
lazyImage.classList.remove('lazy-load');
lazyImageObserver.unobserve(lazyImage);
}
});
});
lazyImages.forEach(function (lazyImage) {
lazyImageObserver.observe(lazyImage);
});
}
});
在此示例中,IntersectionObserver 监视 .lazy-load 图像是否进入视口。检测到图像时,会将其 data-src 属性分配给 src 属性,从而触发实际的图像下载。加载图像后,将删除延迟加载类,并且不会观察到图像。
使用这种简单的延迟加载技术,你可以确保只加载当前查看的图像,减少网络请求的数量并缩短网站的初始加载时间。
注意:将此代码提取到名为 useLazyImageObserver 的自定义组件中可能是个好主意。然后,您可以使用像 Bit 这样的开源工具链通过简单的 npm i @bit/your-username/use-location 在所有项目中发布、版本化和重用它。在这里了解更多。
6、利用缓存
网站性能是提供出色用户体验的关键因素。
提高性能的一项基本技术是缓存,它允许浏览器存储网站资源的副本,例如,图像、样式表和脚本。这减少了重复下载的需要并加快了加载时间。在本节中,我们将探讨缓存的概念以及如何利用它来提高网站的性能。
01)、浏览器缓存
浏览器缓存是一种使网络浏览器能够在本地存储网站文件副本的机制。当用户重新访问你的站点时,浏览器可以从缓存中加载这些资源,而不是再次下载它们,从而加快加载时间并减少服务器负载。
通过配置你的服务器以提供适当的缓存标头,你可以控制缓存哪些资源以及缓存多长时间。
02)、缓存控制和 ETag 标头
用于控制浏览器缓存的两个重要标头是 Cache-Control 和 ETag。
Cache-Control 标头允许你设置缓存指令,例如缓存中资源的最长期限或是否应重新验证。
例如,可以使用 Cache-Control:public, max-age=3600 表示资源可以缓存一小时。
ETag 标头为特定版本的资源提供唯一标识符(通常是哈希)。当浏览器请求资源时,它会发送缓存中的 ETag 值。如果服务器的 ETag 值与浏览器发送的值匹配,则服务器响应 304 Not Modified 状态,浏览器使用缓存的版本。此机制有助于确保浏览器始终拥有最新版本的资源。
03)、在服务器端配置缓存
要启用浏览器缓存,你需要将服务器配置为为你的资源提供适当的标头。此过程因你的服务器软件而异。
例如,在 Apache 服务器中,您可以使用 .htaccess 文件来设置缓存标头:
此配置为 CSS、JS、JPG 和 PNG 文件设置 Cache-Control 标头,允许它们缓存 24 小时。
通过利用浏览器缓存,你可以显着减少用户重新访问您的站点时需要获取的数据量,从而加快加载时间并改善整体用户体验。
7、利用异步加载
随着网站变得越来越复杂,管理 JavaScript 文件的加载对于性能变得越来越重要。
默认情况下,浏览器同步加载脚本,阻塞渲染过程,直到脚本完全加载并执行。异步加载允许脚本与其他资源并行加载,防止它们阻塞渲染并改善整体加载时间。
在本节中,我们将讨论如何利用 JavaScript 文件的异步加载来增强网站的性能。
01)、JavaScript 文件的异步加载
异步加载允许浏览器下载和执行 JavaScript 文件,而不会阻止页面其余部分的呈现。
这种方法不仅可以加快网站的初始呈现速度,还可以降低脚本缓慢或无响应导致延迟的风险。
通过使用 async 和 defer 属性,您可以控制 JavaScript 文件的加载和执行行为。
02)、使用 Async 和 Defer 属性
async 和 defer 属性可以添加到