性能优化与调试技巧 | 豆包MarsCode AI刷题

74 阅读6分钟

一、减少重绘和重排

(一)理解重绘和重排

  • 重排(reflow)
    • 当 DOM 的几何属性(如宽、高、位置等)发生变化时,浏览器需要重新计算元素的几何属性,这个过程就叫做重排。例如,当改变一个元素的宽度、高度、边距、填充或者位置等属性时,会触发重排。
    • 频繁的重排会严重影响性能,因为浏览器需要重新计算页面布局,这是一个比较复杂的过程。
    • 常见引起重排的操作包括添加或删除可见的 DOM 元素、改变元素位置、改变元素尺寸(如改变宽度、高度或内外边距)等。
  • 重绘(repaint)
    • 当元素的外观(如颜色、背景色等非几何属性)发生变化时,浏览器会对元素进行重绘。重绘的成本比重排要低,因为它不涉及布局的重新计算。
    • 不过,如果重绘操作过于频繁,也会对性能产生一定的影响。

(二)减少重绘和重排的策略

  • 合并样式修改
    • 每次修改样式都会引起重排或重绘。如果可以将多个样式修改合并为一次操作,就能减少性能开销。
    • 例如,不要这样写:
const element = document.getElementById('myElement');
element.style.width = '100px';
element.style.height = '100px';
element.style.backgroundColor = 'red';
  • 而是使用类名来一次性修改多个样式:
const element = document.getElementById('myElement');
element.className = 'newStyle';
  • 在 CSS 中定义.newStyle类包含所需的样式:
.newStyle {
    width: 100px;
    height: 100px;
    background - color: red;
}
  • 离线操作 DOM
    • 创建一个文档片段(DocumentFragment),在文档片段中进行 DOM 操作,最后将文档片段添加到实际的 DOM 中。这样可以将多次 DOM 操作合并为一次,减少重排次数。
    • 例如,要创建一个包含多个列表项的无序列表:
const ul = document.createElement('ul');
const fragment = document.createDocumentFragment();
for (let i = 0; i < 10; i++) {
    const li = document.createElement('li');
    li.textContent = 'Item ' + i;
    fragment.appendChild(li);
}
ul.appendChild(fragment);
document.body.appendChild(ul);
  • 避免频繁读取布局属性
    • 当你读取一个元素的布局属性(如 offsetTop、offsetLeft、clientWidth 等)时,浏览器可能会为了返回最新的值而强制进行重排。如果在一个循环或者频繁调用的函数中读取这些属性,会导致大量不必要的重排。
    • 例如,不要这样做:
function loop() {
    for (let i = 0; i < 100; i++) {
        const element = document.getElementById('myElement');
        const offsetTop = element.offsetTop;
        // 其他操作
    }
}
  • 如果确实需要使用布局属性,可以先将属性值缓存起来,避免多次读取导致的重排:
const element = document.getElementById('myElement');
const offsetTop = element.offsetTop;
function loop() {
    for (let i = 0; i < 100; i++) {
        // 使用缓存的offsetTop
        // 其他操作
    }
}

二、使用节流和防抖技术

(一)节流(throttle)

  • 概念
    • 节流的目的是在一定时间内,只允许函数执行一次。例如,当用户滚动页面时,我们可能希望每隔一段时间(如 200 毫秒)才执行一次滚动事件处理函数,而不是在滚动过程中不断地执行,这样可以避免过多的函数调用,减少性能开销。
  • 实现方式
    • 以下是一个简单的节流函数的实现:
function throttle(func, delay) {
    let timer = null;
    return function () {
        if (!timer) {
            func.apply(this, arguments);
            timer = setTimeout(() => {
                timer = null;
            }, delay);
        }
    };
}
  • 例如,对于一个滚动事件处理函数,可以这样使用节流:
const throttledScrollHandler = throttle(function () {
    console.log('Scrolled');
}, 200);
window.addEventListener('scroll', throttledScrollHandler);

(二)防抖(debounce)

  • 概念
    • 防抖是指在事件被触发后,等待一段时间(如 300 毫秒),如果在这段时间内事件没有再次被触发,才执行对应的函数。如果在等待时间内事件再次被触发,那么重新开始等待。
    • 例如,在用户在搜索框中输入内容时,我们可能希望在用户停止输入一段时间后(比如 500 毫秒)才发送搜索请求,而不是用户每输入一个字符就发送请求。
  • 实现方式
    • 以下是一个简单的防抖函数的实现:
function debounce(func, delay) {
    let timer = null;
    return function () {
        if (timer) {
            clearTimeout(timer);
        }
        timer = setTimeout(() => {
            func.apply(this, arguments);
        }, delay);
    };
}
  • 例如,对于一个搜索框的输入事件处理函数,可以这样使用防抖:
const debouncedInputHandler = debounce(function () {
    const inputValue = document.getElementById('searchInput').value;
    console.log('Search for:', inputValue);
}, 500);
document.getElementById('searchInput').addEventListener('input', debouncedInputHandler);

三、使用性能分析工具

(一)浏览器开发者工具

  • Chrome 开发者工具
    • Performance 面板:可以记录一段时间内网页的性能指标,包括 CPU 使用率、内存占用、页面加载时间、脚本执行时间等。通过分析这些数据,可以找到性能瓶颈。例如,你可以看到哪些函数占用了大量的执行时间,是否有频繁的重排和重绘等。
    • Timeline 面板:可以查看页面的加载和渲染过程,包括各个阶段(如解析 HTML、加载 CSS 和 JavaScript、渲染页面等)的时间消耗。它还可以显示事件(如鼠标点击、滚动等)与页面渲染之间的关系,帮助你定位性能问题。
    • Profiler 面板(内存分析) :用于分析 JavaScript 内存使用情况。可以查看内存的分配和回收情况,帮助你发现内存泄漏问题。内存泄漏是指程序中已经不再使用的内存没有被及时释放,导致内存占用不断增加,最终可能会使程序崩溃。
  • Firefox 开发者工具
    • Performance 面板:与 Chrome 类似,提供了网页性能的详细分析。它可以记录页面加载、脚本执行、重排重绘等事件,并以可视化的方式展示时间轴和性能指标,方便开发者查找性能问题。
    • Memory 面板:用于分析内存使用情况,包括堆内存(heap)和栈内存(stack)。可以查看对象的分配和释放情况,帮助检测内存泄漏和内存占用过高的问题。

(二)第三方性能分析工具

  • Lighthouse
    • 这是一个开源的自动化工具,用于提高网页的质量。它可以对网页进行性能、可访问性、最佳实践、搜索引擎优化(SEO)等多个方面的评估。
    • Lighthouse 会生成一个详细的报告,指出网页在各个方面的得分和存在的问题。例如,在性能方面,它会给出页面加载时间、首次内容绘制(FCP)时间、最大内容绘制(LCP)时间等指标的评估,并提供优化建议,如优化图片、减少 JavaScript 阻塞等。
  • WebPageTest
    • 可以在不同的网络环境和浏览器下测试网页的性能。它可以模拟真实用户的访问情况,包括不同的带宽、延迟等网络条件。
    • 该工具会生成详细的性能报告,包括页面加载的各个阶段的时间、资源加载情况等。通过比较不同测试条件下的性能结果,可以找到优化的方向。