提升 JavaScript 性能的技巧 | 豆包MarsCode AI刷题

113 阅读9分钟

在现代的Web开发中,性能优化是一个至关重要的议题。JavaScript作为前端开发的核心语言,其性能直接影响到页面的响应速度和用户体验。尤其是在复杂的用户交互和数据渲染过程中,优化 JavaScript 的执行效率变得尤为重要。在这篇文章中,我们将深入讨论如何通过多种技巧优化 JavaScript 性能,其中包括减少浏览器的重绘和重排、使用节流和防抖技术,以及借助性能分析工具找出瓶颈,来帮助我们编写更高效的代码。

一、减少重绘和重排

1.1 什么是重绘和重排

在学习如何减少重绘和重排之前,我们先了解一下这它们是什么。在浏览器渲染页面时,浏览器会处理DOM树和CSS样式,从而确定页面的外观和布局。在这个过程中,有两个关键操作:

  • 重排(Reflow):当元素的几何属性(如位置、大小、边距等)发生变化时,浏览器会重新计算并更新布局,涉及重新计算元素的大小、位置及其与其他元素的关系。也因此重排的代价比较高,它涉及到了布局树的重建,并且可能影响页面上多个元素。

  • 重绘(Repaint):当元素的外观(如颜色、背景、字体等)发生变化时,浏览器会重新绘制这些元素的外观,但不会重新计算布局。重绘的开销较小,因为它不涉及重建布局树。

因此,为了提升性能,我们在写代码时应尽量避免不必要的重排和重绘。

1.2 如何减少重排和重绘

合理操作 DOM

  • 批量更新 DOM:在写代码时每次对 DOM 进行操作都会触发重排或重绘,因此我们可以尽量将多次操作合并为一次。例如,可以通过使用DocumentFragment或将元素的display属性设置为 none 来暂时隐藏元素,在操作完成后再显示。

  • 避免频繁访问布局信息:访问布局信息(如offsetHeight,offsetWidth, getBoundingClientRect)会强制浏览器进行重排。尽量避免频繁访问这些属性,特别是在短时间内。

  • 避免频繁修改样式:连续修改多个样式属性时,最好将样式批量设置。例如,使用 classList 批量设置类而不是单独修改每个样式属性。

使用 CSS 动画代替 JavaScript 动画

  • 硬件加速:CSS 动画(如 transformopacity)通常会触发合成层(GPU加速),不需要触发布局计算,效率更高。与此相对,JavaScript 动画往往会导致重排和重绘,开销较大。

使用 requestAnimationFrame 代替 setTimeoutsetInterval

  • 使用 requestAnimationFrame可以确保浏览器优化渲染过程,并且避免不必要的重排和重绘。requestAnimationFrame会在浏览器下次重绘时调用回调函数,这使得动画更加流畅。

1.3 例子:减少不必要的重排

// 不优化:多个 DOM 操作会引发多次重排
const div = document.getElementById('myDiv');
div.style.width = '100px';
div.style.height = '200px';
console.log(div.offsetHeight);  // 触发重排
div.style.backgroundColor = 'red';

// 优化:将多次操作合并,减少重排次数
const div = document.getElementById('myDiv');
div.style.width = '100px';
div.style.height = '200px';
div.style.backgroundColor = 'red';  // 一次性设置样式

二、使用节流和防抖技术

2.1 节流和防抖的概念

在处理用户输入或滚动、窗口调整等事件时,我们往往需要限制事件处理程序的调用频率,避免因频繁触发导致性能问题。节流和防抖就是两种常用的事件控制技术:

  • 防抖(Debouncing):防抖的核心思想是延迟执行,即只有在事件停止触发一定时间后,才会执行相应的函数。常用于输入框的实时搜索、表单提交等场景。

  • 节流(Throttling):节流的核心思想是限制频率,即在规定时间内只执行一次事件处理。常用于滚动监听、窗口调整大小等高频事件。

2.2 防抖和节流的实现

防抖(Debounce)示例:

function debounce(func, delay) {
    let timer;
    return function (...args) {
      clearTimeout(timer);
      timer = setTimeout(() => {
        func(...args);
      }, delay);
    };
  }

节流(Throttle)示例:

function throttle(func, limit) {
    let lastFunc;
    let lastTime = 0;
    return function (...args) {
      const now = new Date().getTime();
      if (now - lastTime >= limit) {
        func(...args);
        lastTime = now;
      } else {
        if (lastFunc) {
          clearTimeout(lastFunc);
        }
        lastFunc = setTimeout(() => {
          func(...args);
          lastTime = now;
        }, limit - (now - lastTime));
      }
    };
  }

运行示例: 防抖按钮(点击后延迟一秒才会触发最下面那行显示): image.png 节流按钮(每次点击都会触发操作,但不会在短时间内频繁执行,每 1 秒执行一次): image.png

2.3 节流和防抖的应用场景

防抖的应用场景:

1.输入框的实时搜索:在用户输入搜索关键字时,如果每次用户输入都触发一次请求,可能会导致大量的无效请求,增加服务器的负担。防抖技术可以确保只有当用户停止输入超过一定时间(如 500 毫秒或 1 秒)后,才发送搜索请求,从而减少请求次数,提升用户体验。例如在电商网站的搜索框中,用户输入搜索词时,防抖技术确保只有用户停止输入一段时间后才会发送搜索请求,从而避免频繁请求和页面刷新。

2.按钮点击防止重复提交: 在用户点击提交按钮时,如果没有防止重复点击的机制,可能会导致多次提交相同数据。通过防抖,可以在用户短时间内多次点击同一个按钮时,只响应最后一次点击,避免重复提交或操作。

3.窗口调整大小: 当用户调整浏览器窗口大小时,会频繁触发 resize 事件。为了避免每次调整大小都执行大量的计算或重新渲染,可以使用防抖技术,确保只有当用户停止调整窗口尺寸一段时间后,才进行相关操作。例子如在响应式网页设计中,窗口调整大小时,防抖技术可以减少页面的重绘和计算,使得页面性能更好,不会因为频繁的窗口调整而造成卡顿或无效的操作。

节流的应用场景:

1.滚动监听: 在页面滚动时,如果每次滚动都触发事件处理函数,可能会导致性能问题,尤其是在页面有大量内容或复杂计算时。使用节流可以限制事件触发的频率,确保事件处理函数在规定时间间隔内只执行一次,从而减少不必要的计算,提升页面性能。

2.窗口大小调整 : 在调整浏览器窗口大小时,resize 事件会频繁触发。如果每次触发都执行复杂的计算或更新,可能会导致性能下降,特别是在处理大图像或复杂布局时。节流技术可以使得 resize 事件在特定时间间隔内只触发一次,提升性能。例如在响应式布局中,当用户调整浏览器窗口的大小时,节流可以限制事件触发的频率,避免页面频繁重新计算和渲染布局,提高页面的流畅性。

3.动态加载: 动态加载技术用于延迟加载页面内容(如图片、视频或其他资源)。当用户快速滚动页面时,如果每次滚动都请求数据,可能会导致不必要的请求和性能问题。节流技术可以限制数据请求的频率,确保只有在用户滚动了一定距离后,才会触发下一次数据加载,从而避免多次加载相同的数据。

三、性能分析工具

浏览器开发者工具

浏览器的开发者工具(如DevTools)提供了多种性能分析功能,可以帮助我们深入了解页面的性能瓶颈。

  1. Network 面板

Network 面板帮助开发者查看页面资源的加载情况。通过查看资源的加载时间、请求大小和延迟,可以分析出哪些请求消耗了过多时间。

  1. Memory 面板

Memory 面板可帮助分析页面的内存使用情况,检测内存泄漏。开发者可以通过查看堆快照、监视内存分配等方式,优化内存管理。

image.png

使用 Web Vitals

Web Vitals 是 Google 推出的一套 Web 性能指标,专注于提升用户体验的关键性能指标。主要包括:

  • Largest Contentful Paint (LCP):衡量页面主要内容加载的速度。
  • First Input Delay (FID):衡量用户首次交互时的响应时间。
  • Cumulative Layout Shift (CLS):衡量页面布局稳定性的指标。

我们可以通过 Lighthouse 或者直接集成 Web Vitals 库来监控这些指标。

四、结语

优化 JavaScript 性能是一个多方面的工作,既涉及减少不必要的 DOM 操作和重排,也包括有效的事件控制和合理的资源加载策略。通过合理的技术手段和工具的使用,我们可以有效提升页面的性能,提供更快、更流畅的用户体验。

最终,性能优化并不是一次性工作,而是持续的过程。随着页面功能的增加、用户需求的变化,性能瓶颈可能会不断变化。因此,定期使用性能分析工具来检查应用的表现,及时发现并解决潜在的性能问题,是确保网站持续高效运行的关键。

持续改进与优化

  • 优化是一个长期过程:性能优化不可能一蹴而就。随着页面功能的增多,新的性能问题可能会不断出现。我们需要定期回顾和优化代码,特别是那些需要频繁执行的部分(例如,事件处理函数、动画等)。
  • 用户体验为先:虽然我们可以通过技术手段提高性能,但最重要的是始终以用户体验为核心,确保优化工作能够有效地提升用户的感知速度和操作流畅度。

通过这些技术和方法,我们可以构建出更加高效和用户友好的 Web 应用。优化 Js的性能不仅有助于提升页面的加载速度,还能提升整个网站的响应速度和可访问性,使得网站在竞争激烈的互联网环境中脱颖而出。