一、避免频繁读写 DOM 缓存 DOM 引用 1 2 var dom = document .querySelector('element' );
缓存读取的属性 1 2 3 4 5 6 7 8 9 10 11 setInterval (() => { dom.style.left = parseInt (dom.style.left) + 1 + 'px' ; }, 16 ); let left = parseInt (dom.style.left);setInterval (() => { left++; dom.style.left = left + 'px' ; }, 16 );
合并多次写操作 1 2 3 4 5 6 7 8 9 10 11 for (let i = 0 ; i < 100 ; i++) { document .getElementById('text' ).innerHTML += 'text' + i; } let html = '' ;for (let i = 0 ; i < 100 ; i++) { html += 'text' + i; } document .getElementById('text' ).innerHTML = html;
使用 DocumentFragment 1 2 3 4 5 6 7 const fragment = document .createDocumentFragment();for (let i = 0 ; i < 100 ; i++) { const div = document .createElement('div' ); div.innerHTML = i; fragment.appendChild(div); } document .body.appendChild(fragment);
二、CSS 避免重排重绘 基本原则 使用 className 替代多次 style 操作 使用 visibility:hidden 替代 display:none(前者只触发重绘,后者触发重排) 避免使用 table 布局 对动画元素使用 absolute / fixed 对 resize 事件做防抖节流处理 开启 GPU 渲染动画 以下属性可以避免主线程频繁操作:
transform:不触发 Layout,只触发 Compositeopacity:不触发 Layout,只触发 Compositewill-change:提前告知浏览器优化1 2 3 4 5 div { margin-left : 20px ; transition : margin-left 1s ; }div { transform : translateX (20px ); transition : transform 1s ; }
opacity 优化阴影动画 通过伪元素配合 opacity 过渡替代 box-shadow 过渡:
1 2 3 4 5 6 7 8 div ::before { content : '' ; position : absolute; box-shadow : 0 5px 15px rgba (0 , 0 , 0 , 0.3 ); opacity : 0 ; transition : opacity 1s ; } div :hover ::before { opacity : 1 ; }
will-change 1 2 3 .animate { will-change: transform, opacity; }
使用原则:
不要应用到太多元素上 在变化前通过 JS 添加,变化完成后移除 给浏览器足够的时间做优化准备 三、JS 避免重排重绘 引起回流的属性 读取以下属性会强制触发回流:
1 2 3 4 5 offsetTop / offsetLeft / offsetWidth / offsetHeight scrollTop / scrollLeft / scrollWidth / scrollHeight clientTop / clientLeft / clientWidth / clientHeight getBoundingClientRect() / getClientRects() getComputedStyle()
读写分离 1 2 3 4 5 6 7 8 9 10 11 dom.style.width = '100px' ; console .log(dom.offsetWidth);dom.style.height = '200px' ; console .log(dom.offsetHeight);const width = el.offsetWidth + 10 ;const height = el.offsetHeight + 10 ;el.style.width = width + 'px' ; el.style.height = height + 'px' ;
使用 display:none 离线操作 对于需要多次操作的 DOM 元素,先设置为 display:none,操作完毕再显示:
1 2 3 el.style.display = 'none' ; el.style.display = 'block' ;
使用 cloneNode 1 2 3 const clone = el.cloneNode(true );el.parentNode.replaceChild(clone, el);
四、简化 DOM 结构 DOM 结构越深,内部元素变化引发的祖先重排越多。对于大量数据表格的展示,使用分页而非一次性渲染数万个 DOM。
五、DOM 事件优化 事件委托 1 2 3 4 5 container.addEventListener('click' , (e ) => { const item = e.target.closest('.item' ); if (item) handleClick(item.dataset.id); });
节流 1 2 3 4 5 6 7 8 9 10 11 function throttle (func, wait ) { let previous = 0 ; return function ( ) { const now = +new Date (); if (now - previous > wait) { func.apply(this , arguments ); previous = now; } }; }
防抖 1 2 3 4 5 6 7 8 function debounce (func, wait ) { let timeout; return function ( ) { clearTimeout (timeout); timeout = setTimeout (() => func.apply(this , arguments ), wait); }; }