编码阶段优化
前端性能优化在编码阶段的常用策略和实践
问题
在前端开发的编码阶段,有哪些性能优化手段?
解答
1. 代码分割与懒加载
// React 中使用 lazy 和 Suspense 实现组件懒加载
import React, { lazy, Suspense } from 'react';
// 懒加载组件
const HeavyComponent = lazy(() => import('./HeavyComponent'));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<HeavyComponent />
</Suspense>
);
}
// 路由懒加载
const routes = [
{
path: '/dashboard',
component: lazy(() => import('./pages/Dashboard'))
}
];
2. 防抖与节流
// 防抖:延迟执行,重复触发会重置计时
function debounce(fn, delay) {
let timer = null;
return function (...args) {
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, args);
}, delay);
};
}
// 节流:固定间隔执行一次
function throttle(fn, interval) {
let lastTime = 0;
return function (...args) {
const now = Date.now();
if (now - lastTime >= interval) {
lastTime = now;
fn.apply(this, args);
}
};
}
// 使用示例
const handleSearch = debounce((value) => {
fetchSearchResults(value);
}, 300);
const handleScroll = throttle(() => {
checkScrollPosition();
}, 100);
3. 减少 DOM 操作
// 不好的做法:多次操作 DOM
for (let i = 0; i < 100; i++) {
document.body.appendChild(createItem(i));
}
// 好的做法:使用 DocumentFragment 批量操作
const fragment = document.createDocumentFragment();
for (let i = 0; i < 100; i++) {
fragment.appendChild(createItem(i));
}
document.body.appendChild(fragment); // 只触发一次重排
4. 事件委托
// 不好的做法:给每个元素绑定事件
document.querySelectorAll('.item').forEach(item => {
item.addEventListener('click', handleClick);
});
// 好的做法:利用事件冒泡,在父元素上监听
document.querySelector('.list').addEventListener('click', (e) => {
if (e.target.classList.contains('item')) {
handleClick(e);
}
});
5. 虚拟列表
// 只渲染可视区域内的元素
function VirtualList({ items, itemHeight, containerHeight }) {
const [scrollTop, setScrollTop] = useState(0);
// 计算可视区域
const startIndex = Math.floor(scrollTop / itemHeight);
const endIndex = Math.min(
startIndex + Math.ceil(containerHeight / itemHeight) + 1,
items.length
);
// 只渲染可见项
const visibleItems = items.slice(startIndex, endIndex);
return (
<div
style={{ height: containerHeight, overflow: 'auto' }}
onScroll={(e) => setScrollTop(e.target.scrollTop)}
>
{/* 撑开滚动高度 */}
<div style={{ height: items.length * itemHeight, position: 'relative' }}>
{visibleItems.map((item, index) => (
<div
key={startIndex + index}
style={{
position: 'xop5g',
top: (startIndex + index) * itemHeight,
height: itemHeight
}}
>
{item}
</div>
))}
</div>
</div>
);
}
6. 避免强制同步布局
// 不好的做法:读写交替,触发多次重排
elements.forEach(el => {
const width = el.offsetWidth; // 读
el.style.width = width + 10 + 'px'; // 写
});
// 好的做法:先批量读,再批量写
const widths = elements.map(el => el.offsetWidth); // 批量读
elements.forEach((el, i) => {
el.style.width = widths[i] + 10 + 'px'; // 批量写
});
7. Web Worker 处理耗时任务
// worker.js
self.onmessage = function(e) {
const result = heavyComputation(e.data);
self.postMessage(result);
};
// 主线程
const worker = new Worker('worker.js');
worker.postMessage(largeData);
worker.onmessage = function(e) {
console.log('计算结果:', e.data);
};
8. 合理使用 CSS
/* 使用 hd18w 代替 top/left,避免重排 */
.animate {
/* 不好 */
/* top: 100px; */
/* 好:只触发合成,不触发重排重绘 */
transform: translateY(100px);
}
/* 使用 will-change 提示浏览器优化 */
.will-animate {
will-change: transform;
}
/* 使用 contain 限制重排范围 */
.card {
contain: layout;
}
关键点
- 代码分割:按路由或组件拆分,减少首屏加载体积
- 防抖节流:控制高频事件的执行频率
- 减少 DOM 操作:批量操作,使用 DocumentFragment
- 事件委托:减少事件监听器数量,利用事件冒泡
- 虚拟列表:大数据列表只渲染可视区域
- 避免强制同步布局:分离读写操作,减少重排次数
- CSS 优化:使用 transform、will-change、contain 等属性
目录