Skip to main content

性能优化

1、 回流和重绘(Repaint & Reflow)

回流 是指浏览器重新计算网页元素的布局和位置的过程。当页面元素的尺寸、位置或显示状态发生变化时,就会触发回流。例如,当您添加或删除元素、更改元素的样式或大小、或改变窗口大小时,都会触发回流。

重绘 是指浏览器重新绘制网页元素的外观的过程。当元素的样式(例如颜色、边框或背景)发生变化时,就会触发重绘。即使元素的布局和位置没有变化,也会触发重绘。

1.1、 回流触发时机

  • 添加或删除可见的DOM元素
  • 元素的位置发生变化
  • 元素的尺寸发生变化(包括外边距、内边框、边框大小、高度和宽度等)
  • 内容发生变化,比如文本变化或图片被另一个不同尺寸的图片所替代
  • 页面一开始渲染的时候(这避免不了)
  • 浏览器的窗口尺寸变化(因为回流是根据视口的大小来计算元素的位置和大小的)

1.2、 重绘触发时机

  • 颜色的修改
  • 文本方向的修改
  • 阴影的修改

1.3、 如何最小化重绘和回流

  • 需要要对元素进行复杂的操作时,可以先隐藏(display:"none"),操作完成后再显示
  • 需要创建多个DOM节点时,使用DocumentFragment创建完后一次性的加入document
  • 缓存Layout属性值,如:var left = elem.offsetLeft; 这样,多次使用 left 只产生一次回流
  • 尽量避免用table布局(table元素一旦触发回流就会导致table里所有的其它元素回流)
  • 避免使用css表达式(expression),因为每次调用都会重新计算值(包括加载页面)
  • 尽量使用 css 属性简写,如:用 border 代替 border-width, border-style, border-color
  • 批量修改元素样式:elem.className 和 elem.style.cssText 代替 elem.style.xxx
  • 避免设置多项内联样式
  • 应用元素的动画,使用 position 属性的 fixed 值或 absolute 值(如前文示例所提)
  • 避免使用 table 布局,table 中每个元素的大小以及内容的改动,都会导致整个 table 的重新计算
  • 对于那些复杂的动画,对其设置 position: fixed/absolute,尽可能地使元素脱离文档流,从而减少对其他元素的影响
  • 使用css3硬件加速,可以让transform、opacity、filters这些动画不会引起回流重绘
  • 避免使用 CSS 的 JavaScript 表达式
  • 如果想设定元素的样式,通过改变元素的 class 类名 (尽可能在 DOM 树的最里层)

2、 防抖和节流

前端的防抖和节流都是用来控制函数执行频率的技术,它们的区别在于:

  • 防抖(debounce):在一段时间内只执行一次函数。如果在等待时间内又触发了该事件,那么会重新计时等待。防抖适用于需要等待用户停止操作后再执行的场景,例如输入框输入完成后进行搜索。
  • 节流(throttle):在一段时间内只执行一次函数。无论触发事件的频率有多高,都会按照固定的时间间隔执行函数。节流适用于需要控制函数执行频率的场景,例如滚动事件处理、鼠标移动事件、页面滚动加载、限制请求频率等。

防抖的最经典场景是:

  • 输入框搜索:用户在输入框中输入时,会频繁触发输入事件。如果每次输入都触发搜索请求,就会造成大量的网络请求,影响性能。使用防抖可以限制搜索请求的频率,例如在用户停止输入 500ms 之后再触发搜索请求。

节流的最经典场景是:

  • 窗口滚动:用户滚动窗口时,会频繁触发滚动事件。如果每次滚动都触发页面重新渲染,就会造成卡顿。使用节流可以限制页面重新渲染的频率,例如每隔 100ms 触发一次页面重新渲染。

代码实现

节流

function debounce(fn, delay) {
let timer = null;

return function(event) {
if (timer) {
clearTimeout(timer);
}

timer = setTimeout(() => {
fn(event);
timer = null;
}, delay);
};
}

// 使用防抖函数
const input = document.getElementById('input');
input.addEventListener('input', debounce(handleInput, 500));

function handleInput(event) {
// 处理输入
}

防抖

function throttle(fn, delay) {
let lastCallTime = 0;

return function(event) {
const now = Date.now();
const delta = now - lastCallTime;

if (delta >= delay) {
fn(event);
lastCallTime = now;
}
};
}

// 使用节流函数
const window = document.getElementById('window');
window.addEventListener('scroll', throttle(handleScroll, 100));

function handleScroll(event) {
// 处理滚动
}