DOM 操作优化:
操作 DOM 是很慢的,可以通过减少 DOM 操作次数来优化 JS 的执行速度。
- 将获取到的 DOM 元素通过变量保存起来,这样之后再次使用的时候就不需要重新获取了。
const listEl = document.getElementById('#list') // 推荐
- 新创建的元素,完成必要操作后再添加到页面中。
// 不推荐 for (const item of todoData) { const liEl = document.createElement('li') listEl.appendChild('liEl') // 此时 liEl 元素已经显示在页面中,再对它进行操作会引起浏览器并的重排和重绘 liEl.className = 'item' liEl.innerHTML = item }
// 推荐 for (const item of todoData) { const liEl = document.createElement('li') liEl.className = 'item' liEl.innerHTML = item // 此时 liEl 元素还没有显示在页面中,浏览器并不会对没有显示出来的元素进行重排和重绘 listEl.appendChild('liEl') }
- 使用 DocumentFragment 优化多次的
appendChild()
:可以在一个 DocumentFragment 或者父元素中将要执行的 DOM 操作都执行完,再一次性地添加到 DOM 上。// 不推荐。也是在循环中通过 listEl.appendChild() 多次操作了 listEl 元素 for (const item of todoData) { const liEl = document.createElement('li') liEl.className = 'item' liEl.innerHTML = item // 此时 liEl 元素还没有显示在页面中,浏览器并不会对没有显示出来的元素进行重排和重绘,因此这部分代码对性能影响不大 listEl.appendChild('liEl') }
// 推荐 let liFragment = document.createDocumentFragment() for (const item of todoData) { const liEl = document.createElement('li') liEl.className = 'item' liEl.innerHTML = item liFragment.appendChild('liEl') } listEl.appendChild(liFragment)
- 避免在循环中多次使用 innerHTML,在循环结束后使用一次即可。
// 不推荐 for (const item of todoData) { listEl.innerHTML += `<li class='item'>${item}</li>` }
// 推荐 let html = '' for (const item of todoData) { html += `<li class="item">${item}</li>` } listEl.innerHTML = html
- 不要直接通过 JS 多次修改元素的 style,可以通过一次性添加/移除 class 来修改元素的样式,避免多次的重排与重绘。
// 不推荐。每一行代码都会引发一次浏览器的重排或者重绘 liEl.style.width = '100px' // 重排、重绘 liEl.style.height = '100px' // 重排、重绘 liEl.style.backgroundColor = 'blue' // 重绘
// 推荐。只会引发一次浏览器的重排或者重绘 .li { width: 100px; height: 100px; background-color: blue; } liEl.classList.add('li')
事件优化:
可以通过优化事件来优化 JS 的执行速度。
- 事件委托:如果很多子元素都监听了相同的事件,可以利用事件委托机制,把原本在子元素上监听的事件委托给父元素,让父元素监听。
- 事件稀释:对于高频触发的事件,使用防抖或节流减少执行处理函数的频率。