性能优化与调试技巧 | 青训营

35 阅读2分钟

本篇为实践笔记,性能优化与调试技巧:探讨如何通过优化JavaScript代码来提高性能,包括减少重绘和重排、使用节流和防抖技术、使用性能分析工具等;

优化JavaScript代码可以提高性能并改善用户体验。下面是一些优化技巧:

1. 减少重绘和重排:

  • 使用transform代替topleft等属性来实现平移或动画效果,因为transform不会触发重排。

  • 批量修改DOM后,使用DocumentFragment或离线DOM操作来避免多次重排。

举个例子

// 使用transform代替top、left属性
element.style.transform = 'translateX(100px)';

通过移动等方式进行转移,避免了重绘。

// 批量修改DOM,使用DocumentFragment
const fragment = document.createDocumentFragment();

for (let i = 0; i < 1000; i++) {
  const div = document.createElement('div');
  div.textContent = `Item ${i}`;
  fragment.appendChild(div);
}

document.body.appendChild(fragment);

批量修改DOM,使用DocumentFragment,避免多次重排

2. 使用节流和防抖技术:

  • 对于频繁触发的事件(如scrollresize),使用节流和防抖技术限制事件处理函数的执行次数,减少不必要的计算和渲染。

  • 节流(throttling)通过设置时间间隔来在一定时间内执行一次,防止函数在很短的时间内多次触发。

  • 防抖(debouncing)则是在最后一次触发后等待一段时间再执行,取消前面未执行的操作。

举个例子

// 节流函数
function throttle(func, delay) {
  let timerId;
  
  return function(...args) {
    if (!timerId) {
      timerId = setTimeout(() => {
        func.apply(this, args);
        timerId = null;
      }, delay);
    }
  }
}

window.addEventListener('scroll', throttle(handleScroll, 200));

function handleScroll() {
  // 处理滚动事件
}
// 防抖函数
function debounce(func, delay) {
  let timerId;
  
  return function(...args) {
    clearTimeout(timerId);
    
    timerId = setTimeout(() => {
      func.apply(this, args);
    }, delay);
  }
}

input.addEventListener('input', debounce(handleInput, 300));

function handleInput() {
  // 处理输入事件
}

前端防抖函数和节流函数都是用来优化前端事件处理的方法,它们的主要作用是控制事件的触发频率,以减少不必要的重复执行。

防抖函数会延迟触发事件,并在延迟期间如果再次触发该事件,则重新计时。

例如,当用户连续输入搜索关键字时,可以使用防抖函数来延迟请求发送,只在用户停止输入一段时间后才真正发送请求,避免频繁请求数据。

节流函数会限制触发事件的频率,在一定时间间隔内只能触发一次。

例如,当用户滚动页面时,可以使用节流函数来限制触发事件的频率,以减少滚动事件的处理次数。这样可以减轻浏览器的负担,提高页面的性能和响应速度。

3. 避免不必要的计算和操作:

  • 避免在循环中进行DOM操作,可以将操作放在循环之外或使用虚拟DOM技术。

  • 对于大量数据的渲染,可以考虑使用虚拟滚动(virtual scrolling)或分页加载,减少DOM节点数量。

  • 如果需要频繁修改样式,尽量通过添加/删除CSS类来实现,而不是直接操作样式属性。

举个例子

以虚拟滚动技术举个例子—— image.png

蓝桥杯大赛web前端组曾经就有这样一道模拟题,考察实现如下的效果(图源蓝桥杯官网)——

蓝桥杯:虚拟滚动列表真题

// 使用虚拟滚动技术
const container = document.getElementById('container');
const itemCount = 1000;
const visibleItems = 10;

function renderItems(startIndex) {
  const fragment = document.createDocumentFragment();
  
  for (let i = startIndex; i < startIndex + visibleItems; i++) {
    const div = document.createElement('div');
    div.textContent = `Item ${i}`;
    fragment.appendChild(div);
  }
  
  container.innerHTML = '';
  container.appendChild(fragment);
}

// 根据滚动位置计算需要渲染的起始索引
function handleScroll() {
  const scrollTop = container.scrollTop;
  const startIndex = Math.floor(scrollTop / 20);
  renderItems(startIndex);
}

container.addEventListener('scroll', handleScroll);

在这段代码中,容器元素 container 用于显示滚动内容。itemCount 表示总共的项目数量,visibleItems 表示在可视区域内同时显示的项目数量。

renderItems 函数根据起始索引 startIndex 渲染可见区域的项目。它通过创建文档片段 fragment,并循环创建项目元素,然后将它们添加到文档片段中。最后,清空容器的内容,并将文档片段添加到容器中,完成渲染。

handleScroll 函数监听容器的滚动事件,根据滚动位置计算需要渲染的起始索引,并调用 renderItems 函数进行渲染。

通过使用虚拟滚动技术,只有可见区域内的项目才会被渲染,从而减少了DOM操作和页面布局,提高了前端性能和用户体验。这在处理大量数据时尤其有效,可以避免页面卡顿和资源浪费。

4. 使用合适的数据结构和算法:

  • 对于数组操作,可以使用mapfilterreduce等高阶函数替代传统的循环。

  • 对于查找和插入操作频繁的场景,考虑使用Map或Set来提高查找效率。