您现在的位置是:网站首页 > 频繁操作场景下的模式优化文章详情
频繁操作场景下的模式优化
陈川
【
JavaScript
】
11436人已围观
10390字
频繁操作场景下的模式优化
高频触发的事件(如滚动、输入、窗口调整)容易导致性能问题。以搜索框输入为例,每次按键都会触发请求,短时间内产生大量无效调用。这种场景需要特定设计模式平衡响应速度与资源消耗。
防抖模式
防抖的核心在于延迟执行,直到操作停止超过指定时间。适用于连续触发但只需最终结果的场景,比如搜索建议。
function debounce(func, delay) {
let timer;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => {
func.apply(this, args);
}, delay);
};
}
// 实际应用
const searchInput = document.getElementById('search');
searchInput.addEventListener('input', debounce(function() {
fetchResults(this.value);
}, 300));
高级实现可添加立即执行选项。首次触发立即执行,后续操作进入防抖周期:
function advancedDebounce(func, delay, immediate) {
let timer;
return function(...args) {
const context = this;
const later = () => {
timer = null;
if (!immediate) func.apply(context, args);
};
const callNow = immediate && !timer;
clearTimeout(timer);
timer = setTimeout(later, delay);
if (callNow) func.apply(context, args);
};
}
节流模式
节流通过固定频率执行来限制调用次数。适用于持续触发但需要定期反馈的场景,比如滚动位置计算。
基础时间戳实现:
function throttle(func, limit) {
let lastRun;
return function(...args) {
const now = Date.now();
if (!lastRun || (now - lastRun >= limit)) {
func.apply(this, args);
lastRun = now;
}
};
}
定时器增强版保证尾调用执行:
function enhancedThrottle(func, limit) {
let lastFunc;
let lastRan;
return function(...args) {
const context = this;
if (!lastRan) {
func.apply(context, args);
lastRan = Date.now();
} else {
clearTimeout(lastFunc);
lastFunc = setTimeout(function() {
if ((Date.now() - lastRan) >= limit) {
func.apply(context, args);
lastRan = Date.now();
}
}, limit - (Date.now() - lastRan));
}
};
}
双模式结合策略
复杂场景可能需要混合策略。比如视频播放器同时处理进度更新和缓冲检测:
function hybridControl(mainAction, secondaryAction, delay) {
let lastMainCall = 0;
let pendingSecondary = null;
return function(...args) {
const now = Date.now();
// 主操作立即执行但限制频率
if (now - lastMainCall >= delay) {
mainAction.apply(this, args);
lastMainCall = now;
}
// 次要操作防抖处理
clearTimeout(pendingSecondary);
pendingSecondary = setTimeout(() => {
secondaryAction.apply(this, args);
}, delay * 2);
};
}
动画帧优化
对于视觉变化操作,requestAnimationFrame 比固定时间间隔更符合浏览器渲染节奏:
function rafThrottle(func) {
let ticking = false;
return function(...args) {
if (!ticking) {
requestAnimationFrame(() => {
func.apply(this, args);
ticking = false;
});
ticking = true;
}
};
}
// 滚动事件优化
window.addEventListener('scroll', rafThrottle(function() {
updateParallaxElements();
}));
批量处理模式
当面对大量数据更新时,采用批量提交策略:
class BatchProcessor {
constructor(processor, batchSize = 50) {
this.queue = [];
this.batchSize = batchSize;
this.processor = processor;
}
add(item) {
this.queue.push(item);
if (this.queue.length >= this.batchSize) {
this.flush();
}
}
flush() {
if (this.queue.length > 0) {
this.processor([...this.queue]);
this.queue = [];
}
}
}
// 使用示例
const logBatch = new BatchProcessor(items => {
console.log('Processing batch:', items);
});
for (let i = 0; i < 1000; i++) {
logBatch.add(i);
}
观察者模式优化
高频事件源使用观察者模式时,通过中间层控制通知频率:
class BufferedObserver {
constructor(subject, bufferTime) {
this.subscribers = new Set();
this.buffer = [];
this.timer = null;
subject.subscribe(data => {
this.buffer.push(data);
if (!this.timer) {
this.timer = setTimeout(() => {
this.notifyAll();
this.timer = null;
}, bufferTime);
}
});
}
subscribe(callback) {
this.subscribers.add(callback);
}
notifyAll() {
if (this.buffer.length > 0) {
const snapshot = [...this.buffer];
this.buffer = [];
this.subscribers.forEach(cb => cb(snapshot));
}
}
}
虚拟化处理
对于超长列表渲染,采用虚拟滚动技术:
class VirtualScroller {
constructor(container, itemHeight, renderItem) {
this.container = container;
this.itemHeight = itemHeight;
this.renderItem = renderItem;
this.data = [];
this.visibleItems = [];
this.scrollTop = 0;
this.container.style.height = `${itemHeight * this.data.length}px`;
this.viewport = document.createElement('div');
this.viewport.style.position = 'relative';
this.viewport.style.height = `${container.clientHeight}px`;
this.container.appendChild(this.viewport);
this.container.addEventListener('scroll', () => {
this.handleScroll();
});
}
setData(newData) {
this.data = newData;
this.container.style.height = `${this.itemHeight * this.data.length}px`;
this.updateVisibleItems();
}
handleScroll() {
this.scrollTop = this.container.scrollTop;
this.updateVisibleItems();
}
updateVisibleItems() {
const startIdx = Math.floor(this.scrollTop / this.itemHeight);
const endIdx = Math.min(
startIdx + Math.ceil(this.container.clientHeight / this.itemHeight),
this.data.length
);
this.visibleItems = this.data.slice(startIdx, endIdx);
this.renderViewport(startIdx);
}
renderViewport(offset) {
this.viewport.style.transform = `translateY(${offset * this.itemHeight}px)`;
this.viewport.innerHTML = '';
this.visibleItems.forEach((item, i) => {
const element = this.renderItem(item);
element.style.position = 'absolute';
element.style.top = `${i * this.itemHeight}px`;
this.viewport.appendChild(element);
});
}
}
缓存策略优化
高频计算场景引入缓存机制:
function createCachedSelector(selectors, transformer) {
const cache = new WeakMap();
return function(state) {
if (!cache.has(state)) {
const inputs = selectors.map(selector => selector(state));
cache.set(state, transformer(...inputs));
}
return cache.get(state);
};
}
// 使用示例
const getFilteredUsers = createCachedSelector(
[state => state.users, state => state.filters],
(users, filters) => {
return users.filter(user =>
filters.every(filter => filter(user))
);
}
);
事件委托优化
大量相似元素的事件处理改用事件委托:
document.getElementById('grid-container').addEventListener('click', function(event) {
const cell = event.target.closest('.grid-cell');
if (cell) {
const row = cell.parentNode;
const colIndex = Array.from(row.children).indexOf(cell);
const rowIndex = Array.from(this.children).indexOf(row);
handleCellClick(rowIndex, colIndex, cell);
}
});
// 替代方案:为每个单元格单独添加监听器
document.querySelectorAll('.grid-cell').forEach(cell => {
cell.addEventListener('click', function() {
const row = this.parentNode;
const colIndex = Array.from(row.children).indexOf(this);
const rowIndex = Array.from(row.parentNode.children).indexOf(row);
handleCellClick(rowIndex, colIndex, this);
});
});
Web Worker 分流
将密集型计算转移到 Web Worker:
// 主线程代码
const worker = new Worker('compute.js');
worker.onmessage = function(e) {
updateUI(e.data);
};
function requestComputation(data) {
worker.postMessage(data);
}
// compute.js
self.onmessage = function(e) {
const result = heavyComputation(e.data);
self.postMessage(result);
};
function heavyComputation(data) {
// 复杂计算逻辑
return processedData;
}
惰性加载模式
资源按需加载优化初始化性能:
class LazyLoader {
constructor(loader) {
this.loader = loader;
this.promise = null;
this.value = null;
}
get() {
if (this.value !== null) return Promise.resolve(this.value);
if (this.promise !== null) return this.promise;
this.promise = this.loader().then(val => {
this.value = val;
return val;
});
return this.promise;
}
}
// 使用示例
const imageLoader = new LazyLoader(() =>
import('./heavy-image-processor')
);
document.getElementById('process-btn').addEventListener('click', () => {
imageLoader.get().then(processor => {
processor.transformImages();
});
});
增量处理模式
大数据集采用分片处理策略:
async function processInChunks(items, chunkSize, processItem) {
for (let i = 0; i < items.length; i += chunkSize) {
const chunk = items.slice(i, i + chunkSize);
await Promise.all(chunk.map(item =>
new Promise(resolve => {
requestIdleCallback(() => {
processItem(item);
resolve();
});
})
));
// 允许主线程响应
await new Promise(resolve => setTimeout(resolve, 0));
}
}
// 使用示例
processInChunks(largeDataset, 100, item => {
renderDataItem(item);
});
状态批处理
React 等框架中的状态批量更新:
class BatchStateUpdater {
constructor(component) {
this.component = component;
this.pendingStates = [];
this.isBatching = false;
}
setState(update) {
this.pendingStates.push(update);
if (!this.isBatching) {
this.isBatching = true;
Promise.resolve().then(() => {
this.applyUpdates();
this.isBatching = false;
});
}
}
applyUpdates() {
if (this.pendingStates.length > 0) {
this.component.setState(prevState =>
this.pendingStates.reduce(
(state, update) => ({ ...state, ...update }),
prevState
)
);
this.pendingStates = [];
}
}
}
// 在React组件中使用
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.updater = new BatchStateUpdater(this);
}
handleMultipleUpdates = () => {
this.updater.setState({ count: 1 });
this.updater.setState({ loading: true });
this.updater.setState({ count: 2 });
// 最终只触发一次渲染
};
}
时间切片技术
使用 generator 实现任务分片:
function* taskSlicer(tasks) {
for (const task of tasks) {
const start = performance.now();
yield task();
// 每执行16ms就让出控制权
if (performance.now() - start > 16) {
yield;
}
}
}
async function runSlicedTasks(tasks) {
const slicer = taskSlicer(tasks);
function processNext() {
const { value, done } = slicer.next();
if (done) return;
Promise.resolve(value).finally(() => {
requestIdleCallback(processNext);
});
}
processNext();
}
// 使用示例
runSlicedTasks([
() => processLargeDataChunk(0),
() => processLargeDataChunk(1),
// ...更多任务
]);
上一篇: 垃圾回收机制对设计模式的影响
下一篇: 移动端环境下的模式调整