爱姥爷 發表於 2025-11-8 17:07:00

JavaScript性能优化:我从50ms降到5ms的7个关键技巧

<h1 data-id="heading-0">🧑‍💻 写在开头</h1>
<p>点赞 + 收藏 === 学会🤣🤣🤣</p>
<h2 data-id="heading-1">引言</h2>
<blockquote>
<p>在现代Web开发中,性能优化是一个永恒的话题。随着前端应用的复杂度不断提升,JavaScript的执行效率直接影响用户体验。我曾面临一个关键功能的性能瓶颈——初始实现需要50ms完成的任务,通过一系列优化手段成功降至5ms。本文将分享这7个关键技巧,涵盖从代码层面到运行时优化的全方位实践。</p>
</blockquote>
<h2 data-id="heading-2">主体</h2>
<h3 data-id="heading-3">1. 减少DOM操作:批量处理与文档片段</h3>
<blockquote>
<p>DOM操作是JavaScript中最昂贵的操作之一。频繁的DOM更新会导致重排(Reflow)和重绘(Repaint),严重影响性能。</p>
</blockquote>
<p>优化前:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">for (let i = 0; i &lt; 1000; i++) {
const div = document.createElement('div');
document.body.appendChild(div);
}</pre>
</div>
<p>优化后:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">const fragment = document.createDocumentFragment();
for (let i = 0; i &lt; 1000; i++) {
const div = document.createElement('div');
fragment.appendChild div);
}
document.body.appendChild(fragment);</pre>
</div>
<p>关键点:</p>
<blockquote>
<ul>
<li>使用<code>document.createDocumentFragment()</code>创建离线DOM节点</li>
<li>一次性插入而非多次单独插入</li>
<li>Virtual DOM库(如React)的核心原理正是基于此</li>
</ul>
</blockquote>
<h3 data-id="heading-4">2. 事件委托:减少事件监听器数量</h3>
<p>每个事件监听器都会占用内存和处理时间。当页面中存在大量相似元素的交互时,事件委托能显著提升性能。</p>
<p>优化前:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">document.querySelectorAll('.btn').forEach(btn =&gt; {
btn.addEventListener('click', handleClick);
});</pre>
</div>
<p>优化后:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">document.body.addEventListener('click', (e) =&gt; {
if (e.target.classList.contains('btn')) {
    handleClick(e);
}
});</pre>
</div>
<p>进阶技巧:</p>
<ul>
<li>对于动态内容尤其有效</li>
<li>CSS选择器匹配可以使用<code>matches()</code>方法做更复杂的判断</li>
</ul>
<h3 data-id="heading-5">3. Web Workers:将耗时任务移出主线程</h3>
<p>长时间运行的JavaScript会阻塞UI渲染。Web Workers允许我们在后台线程执行CPU密集型任务。</p>
<p>典型应用场景:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">// main.js
const worker = new Worker('worker.js');
worker.postMessage(data);
worker.onmessage = (e) =&gt; processResults(e.data);

// worker.js
onmessage = (e) =&gt; {
const result = heavyComputation(e.data);
postMessage(result);
};</pre>
</div>
<p>注意事项:</p>
<blockquote>
<ul>
<li>Worker间通信存在序列化/反序列化开销</li>
<li>DOM API在Worker中不可用</li>
<li>SharedArrayBuffer可实现高效内存共享</li>
</ul>
</blockquote>
<h3 data-id="heading-6">4. Memoization:缓存函数计算结果</h3>
<p>对于纯函数和计算密集型操作,缓存机制可以避免重复计算。</p>
<p>基础实现:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">function memoize(fn) {
const cache = new Map();
return (...args) =&gt; {
   const key = JSON.stringify(args);
   if (cache.has(key)) return cache.get(key);
   const result = fn(...args);
   cache.set(key, result);
   return result;
};
}</pre>
</div>
<p>React生态中的<code>useMemo</code>和<code>useCallback</code>就是这一思想的体现。对于递归算法(如斐波那契数列),memoization可以将时间复杂度从O(2^n)降至O(n)。</p>
<h3 data-id="heading-7">5. requestAnimationFrame vs setTimeout</h3>
<p>动画和视觉更新应始终使用<code>requestAnimationFrame(rAF)</code>而非定时器:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">function animate() {
// Animation logic...
requestAnimationFrame(animate);
}
animate();</pre>
</div>
<p>优势对比:</p>
<p><img src="https://img2024.cnblogs.com/blog/2149129/202511/2149129-20251108170431451-2080400272.png" alt="企业微信截图_20251108165444" loading="lazy"></p>
<h3>6. Typed Arrays处理二进制数据</h3>
<p>当处理Canvas、WebGL或Web Audio等API时,Typed Array比常规数组快3-10倍:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">// Float32Array比普通数组快5倍以上
const data = new Float32Array(1000000);

// SIMD运算示例
function simdSum(a, b) {
const vecA = SIMD.Float32x4.load(a, );
const vecB = SIMD.Float32x4.load(b, );
const sum SIMD.Float32x4.add(vecA, vecB);
return sum;
}</pre>
</div>
<p>浏览器会对Typed Array进行特殊优化: -连续内存分配<br>-预知数据类型<br>-兼容SIMD指令集</p>
<h3>7. V8隐藏类优化</h3>
<p>V8引擎通过"隐藏类"机制加速属性访问,不当的对象操作会破坏这种优化:</p>
<p>❌&nbsp;破坏隐藏类的写法:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">function Point(x, y) { this.x x; this.y y }
const p new Point(1,2);
p.z=3;//此时隐藏类变更</pre>
</div>
<p>✅&nbsp;保持隐藏类的写法:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">const p new Point(1,2);//所有实例共享相同隐藏类   </pre>
</div>
<p>最佳实践包括: -构造函数中初始化所有属性<br>-避免动态添加属性<br>-按相同顺序创建属性</p>
<h2>高级技巧补充</h2>
<h3>利用WASM处理极限性能场景</h3>
<p>对于音视频编解码、物理引擎等场景,WebAssembly可以提供接近原生代码的性能:</p>
<p><code>cpp//fibonacci.cpp extern "C" { int fib(int n){return(n&lt;2)?1:fib(n1)+fib(n2);}}</code></p>
<p>编译为WASM后调用效率比JS实现高200%。</p>
<h3>GPU加速via WebGL</h3>
<p>通过将通用计算转化为纹理操作可以实现GPU加速:&nbsp;<code>glslprecision highp float;uniform sampler2D inputTexture;[...]void main(){vec4 data texture2D(inputTexture,[...]);gl_FragColor process(data;}</code></p>
<p>适用于图像处理、矩阵运算等并行计算场景。</p>
<h2>总结</h2>
<p>从50ms到5ms的性能飞跃并非魔法,而是对JavaScript运行时特性的深入理解与合理利用的结果。本文介绍的7个核心技巧构成了现代前端性能优化的知识框架:</p>
<blockquote>
<p>1.DOM操作的批量处理<br>2.事件委托机制<br>3.Web Workers多线程<br>4.Memoization缓存<br>5.rAF动画时序控制<br>6.Typed Arrays高效存储<br>7.V8隐藏类友好编码</p>
</blockquote>
<h3 id="tid-D8HBxE">如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。</h3>
<p><em><img src="https://img2024.cnblogs.com/blog/2149129/202501/2149129-20250122165814748-630765389.png" alt="" loading="lazy"></em></p><br><br>
来源:https://www.cnblogs.com/smileZAZ/p/19202690
頁: [1]
查看完整版本: JavaScript性能优化:我从50ms降到5ms的7个关键技巧