ホームページ > ウェブフロントエンド > jsチュートリアル > コードの実行が遅いですか?プログラムの高速実行を維持するには、JavaScript と Node.js でよくある 19 の間違いを回避してください

コードの実行が遅いですか?プログラムの高速実行を維持するには、JavaScript と Node.js でよくある 19 の間違いを回避してください

DDD
リリース: 2023-09-22 10:50:48
オリジナル
1581 人が閲覧しました

Web サイトが遅い、または遅れているのはアマチュアの兆候ですが、スムーズで最適化されたエクスペリエンスはユーザーを喜ばせ、プロとは一線を画します。

しかし、本当に高パフォーマンスの Web アプリケーションを作成するには、落とし穴がたくさんあります。バグはたくさんあり、気付かないうちに JavaScript の速度を低下させる可能性があります。小さな見落としがあるとコードが肥大化し、静かに少しずつ速度が低下する可能性があります。

これはどうなりましたか?

意図せずに JavaScript の速度を低下させる一般的な方法が多数あることが判明しました。時間が経つと、これにより Web サイトのパフォーマンスが妨げられる可能性があります。

これらの間違いは避けることができます。

今日は、JavaScript および Node.js アプリケーションの速度を静かに低下させる可能性がある 19 のパフォーマンスの落とし穴に焦点を当てます。コードを最適化するための具体的な例と実用的な解決策を使用して、これらの問題の原因を探ります。

ユーザーを満足させるスムーズな Web エクスペリエンスを作成するには、これらの危険を特定して排除することが重要です。それでは、詳しく見ていきましょう!

1. 間違った変数宣言とスコープ

JavaScript を初めて学習する場合、すべての変数をグローバルに宣言するのは簡単です。ただし、これは将来的に問題を引き起こす可能性があります。例を見てみましょう:

// globals.js
var color = 'blue';
function printColor() {
  console.log(color); 
}
printColor(); // Prints 'blue'
ログイン後にコピー

これは正常に動作しますが、別のスクリプトをロードした場合を想像してください:

// script2.js
var color = 'red';
printColor(); // Prints 'red'!
ログイン後にコピー

color はグローバルなので、script2.js がそれをオーバーライドします。この問題を解決するには、可能な限り関数内で変数を宣言します。

function printColor() {
  var color = 'blue'; // local variable
  
  console.log(color);
}
printColor(); // Prints 'blue'
ログイン後にコピー

他のスクリプトからの変更は printColor に影響しません。

不必要な場合にグローバル スコープで変数を宣言するのはアンチパターンです。グローバル変数を構成定数に限定してみてください。他の変数については、可能な限り最小のスコープでローカルに宣言します。

2. 非効率的な DOM 操作

DOM 要素を更新するときは、一度に 1 つのノードを操作するのではなく、バッチ変更を行ってください。次の例を考えてみましょう:

const ul = document.getElementById('list');
for (let i = 0; i < 10; i++) {
  const li = document.createElement(&#39;li&#39;);
  li.textContent = i;
  
  ul.appendChild(li);
}
ログイン後にコピー

これにより、リスト項目が 1 つずつ追加されます。最初に文字列を構築してから、.innerHTML を設定することをお勧めします。

const ul = document.getElementById(&#39;list&#39;);
let html = &#39;&#39;;
for (let i = 0; i < 10; i++) {
  html += `<li>${i}</li>`; 
}
ul.innerHTML = html;
ログイン後にコピー

文字列を構築すると、リフローが最小限に抑えられます。 DOM を 10 回ではなく 1 回更新します。

複数の更新の場合は、変更をビルドして最後に適用します。さらに良いのは、DocumentFragment を使用してバッチ追加することです。

3. 過剰な DOM 操作

DOM を頻繁に更新すると、パフォーマンスが低下します。ページにメッセージを挿入するチャット アプリケーションを考えてみましょう。

否定的な例:

// New message received
const msg = `<div>${messageText}</div>`;
chatLog.insertAdjacentHTML(&#39;beforeend&#39;, msg);
ログイン後にコピー

これはすべてのメッセージに単純に挿入されます。更新を制限することをお勧めします:

正しい例:

let chatLogHTML = &#39;&#39;;
const throttleTime = 100; // ms
// New message received  
chatLogHTML += `<div>${messageText}</div>`;
// Throttle DOM updates
setTimeout(() => {
  chatLog.innerHTML = chatLogHTML;
  chatLogHTML = &#39;&#39;; 
}, throttleTime);
ログイン後にコピー

現在、更新は最大 100 ミリ秒ごとに行われるため、DOM 操作が低く抑えられます。

非常に動的な UI の場合は、React のような仮想 DOM ライブラリを検討してください。これらにより、仮想表現を使用した DOM 操作が最小限に抑えられます。

4. アクティビティ委任の欠如

イベント リスナーを多くの要素にアタッチすると、不要なオーバーヘッドが発生します。各行に削除ボタンがあるテーブルを考えてみましょう:

否定的な例:

const rows = document.querySelectorAll(&#39;table tr&#39;);
rows.forEach(row => {
  const deleteBtn = row.querySelector(&#39;.delete&#39;);  
  deleteBtn.addEventListener(&#39;click&#39;, handleDelete);
});
ログイン後にコピー

これにより、各削除ボタンにリスナーが追加されます。イベント委任をより適切に使用する必要があります:

正しい例:

const table = document.querySelector(&#39;table&#39;);
table.addEventListener(&#39;click&#39;, e => {
  if (e.target.classList.contains(&#39;delete&#39;)) {
    handleDelete(e);
  }
});
ログイン後にコピー

現在、.net にはリスナーが 1 つだけあり、メモリ オーバーヘッドが少なくなります。

イベント委任ではイベント バブリングを使用します。リスナーは複数の子孫からのイベントを処理できます。該当する場合は常に委任を使用します。

5. 非効率的な文字列の連結

ループ内で文字列を連結すると、パフォーマンスに影響します。次のコードを考えてみましょう:

let html = &#39;&#39;;
for (let i = 0; i < 10; i++) {
  html += &#39;<div>&#39; + i + &#39;</div>&#39;;
}
ログイン後にコピー

新しい文字列を作成するには、メモリを割り当てる必要があります。配列を使用することをお勧めします。

const parts = [];
for (let i = 0; i < 10; i++) {
  parts.push(&#39;<div>&#39;, i, &#39;</div>&#39;);
}
const html = parts.join(&#39;&#39;);
ログイン後にコピー

配列を構築すると、中間文字列の数が最小限に抑えられます。 .join() は最後に結合します。

複数の文字列を追加するには、配列連結を使用してください。また、値を埋め込むテンプレート リテラルも考慮してください。

6. 最適化されていないループ

JavaScript のループは、パフォーマンスの問題を引き起こすことがよくあります。よくある間違いは、配列の長さに繰り返しアクセスすることです。

反例:

const items = [/*...*/];
for (let i = 0; i < items.length; i++) {
  // ...
}
ログイン後にコピー

.length の冗長チェックにより、最適化が妨げられる可能性があります。

正しい例:

const items = [/*...*/];  
const len = items.length;
for (let i = 0; i < len; i++) {
  // ...
}
ログイン後にコピー

キャッシュの長さにより速度が向上します。その他の最適化には、ループ外の不変条件の除去、終了条件の簡素化、反復内での負荷の高い操作の回避などが含まれます。

7. 不要な同期操作

JavaScript 的异步功能是一个关键优势。但要小心阻塞 I/O!例如:

反面例子:

const data = fs.readFileSync(&#39;file.json&#39;); // blocks!
ログイン後にコピー

这会在从磁盘读取时停止执行。相反,如果使用回调或承诺:

正确示例:

fs.readFile(&#39;file.json&#39;, (err, data) => {
  // ...
});
ログイン後にコピー

现在,事件循环在读取文件时继续。对于复杂的流程,async/await简化异步逻辑。避免同步操作以防止阻塞。

8. 阻止事件循环

JavaScript 使用单线程事件循环。阻止它会停止执行。一些常见的拦截器:

繁重的计算任务

同步输入/输出

未优化的算法

例如:

function countPrimes(max) {
  // Unoptimized loop
  for (let i = 0; i <= max; i++) {
    // ...check if prime...
  }
}
countPrimes(1000000); // Long running!
ログイン後にコピー

这会同步执行,并阻止其他事件。避免:

推迟不必要的工作

批量数据处理

使用工作线程

寻找优化机会

保持事件循环顺利运行。定期分析以捕获阻塞代码。

9. 错误处理效率低下

在 JavaScript 中正确处理错误至关重要。但要小心性能陷阱!

反面例子:

try {
  // ...
} catch (err) {
  console.error(err); // just logging
}
ログイン後にコピー

这会捕获错误但不采取纠正措施。未处理的错误通常会导致内存泄漏或数据损坏。

正确示例:

try {
  // ...
} catch (err) {
  console.error(err);
  
  // Emit error event 
  emitError(err); 
  
  // Nullify variables
  obj = null;
  
  // Inform user
  showErrorNotice();
}
ログイン後にコピー

记录还不够!清理工件、通知用户并考虑恢复选项。使用 Sentry 等工具来监控生产中的错误。明确处理所有错误。

10. 内存泄漏

当内存被分配但从未释放时,就会发生内存泄漏。随着时间的推移,泄漏会累积并降低性能。

JavaScript 中的常见来源包括:

未清理的事件监听器

对已删除 DOM 节点的过时引用

不再需要的缓存数据

闭包中的累积状态

例如:

function processData() {
  const data = [];
  // Use closure to accumulate data
  return function() {
    data.push(getData()); 
  }
}
const processor = processData();
// Long running...keeps holding reference to growing data array!
ログイン後にコピー

数组不断变大,但从未被清除。修理:

使用弱引用

清理事件监听器

删除不再需要的引用

限制关闭状态大小

监视内存使用情况并观察增长趋势。在泄漏堆积之前主动消除泄漏。

11. 过度使用依赖项

虽然 npm 提供了无穷无尽的选择,但请抵制过度导入的冲动!每个依赖项都会增加包大小和攻击面。

反面例子:

import _ from &#39;lodash&#39;;
import moment from &#39;moment&#39;; 
import validator from &#39;validator&#39;;
// etc...
ログイン後にコピー

为次要实用程序导入整个库。最好根据需要挑选助手:

正确示例:

import cloneDeep from &#39;lodash/cloneDeep&#39;;
import { format } from &#39;date-fns&#39;;
import { isEmail } from &#39;validator&#39;;
ログイン後にコピー

只导入您需要的内容。定期检查依赖关系以删除未使用的依赖关系。保持捆绑精简并最大限度地减少依赖性。

12. 缓存不足

缓存允许通过重用先前的结果来跳过昂贵的计算。但它经常被忽视。

反面例子:

function generateReport() {
  // Perform expensive processing
  // to generate report data... 
}
generateReport(); // Computes
generateReport(); // Computes again!
ログイン後にコピー

由于输入没有更改,因此可以缓存报告:

正确示例:

let cachedReport;
function generateReport() {
  if (cachedReport) {
    return cachedReport;
  }
  cachedReport = // expensive processing...
  return cachedReport; 
}
ログイン後にコピー

现在,重复调用速度很快。

13. 未优化的数据库查询

与数据库交互时,低效的查询可能会降低性能。需要避免的一些问题:

反面例子:

// No indexing
db.find({name: &#39;John&#39;, age: 35}); 
// Unecessary fields
db.find({first: &#39;John&#39;, last:&#39;Doe&#39;, email:&#39;john@doe.com&#39;}, {first: 1, last: 1});
// Too many separate queries
for (let id of ids) {
  const user = db.find({id});
}
ログイン後にコピー

这无法利用索引、检索未使用的字段并执行过多的查询。

正确示例:

// Use index on &#39;name&#39; 
db.find({name: &#39;John&#39;}).hint({name: 1});
// Only get &#39;email&#39; field
db.find({first: &#39;John&#39;}, {email: 1}); 
// Get users in one query
const users = db.find({
  id: {$in: ids} 
});
ログイン後にコピー

分析并解释计划。战略性地创建索引。避免多次零散的查询。优化数据存储交互。

14. Promise 中错误处理不当

Promise 简化了异步代码。但未经处理的拒绝就是无声的失败!

反面例子:

function getUser() {
  return fetch(&#39;/user&#39;)
    .then(r => r.json()); 
}
getUser();
ログイン後にコピー

如果fetch拒绝,异常就不会被注意到。

正确示例:

function getUser() {
  return fetch(&#39;/user&#39;)
    .then(r => r.json())
    .catch(err => console.error(err));
} 
getUser();
ログイン後にコピー

链接.catch()可以正确处理错误。

15. 同步网络操作

网络请求应该是异步的。但有时会使用同步变体:

反面例子:

const data = http.getSync(&#39;http://example.com/data&#39;); // blocks!
ログイン後にコピー

这会在请求期间停止事件循环。相反,使用回调:

正确示例:

http.get(&#39;http://example.com/data&#39;, res => {
  // ...
});
ログイン後にコピー

或者:

fetch(&#39;http://example.com/data&#39;)
  .then(res => res.json())
  .then(data => {
    // ...
  });
ログイン後にコピー

异步网络请求允许在等待响应时进行其他处理。避免同步网络调用。

16. 低效的文件 I/O 操作

读/写文件同步阻塞。例如:

反面例子:

const contents = fs.readFileSync(&#39;file.txt&#39;); // blocks!
ログイン後にコピー

这会在磁盘 I/O 期间停止执行。

正确示例:

fs.readFile(&#39;file.txt&#39;, (err, contents) => {
  // ...
});
// or promises
fs.promises.readFile(&#39;file.txt&#39;)
   .then(contents => {
     // ...  
   });
ログイン後にコピー

这允许事件循环在文件读取期间继续。

对于多个文件,使用流:

function processFiles(files) {
  for (let file of files) {
    fs.createReadStream(file)
      .pipe(/*...*/);
  }
}
ログイン後にコピー

避免同步文件操作。使用回调、promise 和流。

17. 忽略性能分析和优化

在出现明显问题之前,很容易忽视性能。但优化应该持续进行!首先使用分析工具进行测量:

浏览器开发工具时间线

Node.js 分析器

第三方分析器

即使性能看起来不错,这也揭示了优化机会:

// profile.js
function processOrders(orders) {
  orders.forEach(o => {
    // ...
  });
}
processOrders(allOrders);
ログイン後にコピー

分析器显示processOrders需要 200 毫秒。

分析指导优化。制定绩效预算,如果超出则失败。经常测量并明智地优化。

18. 不利用缓存机制

缓存通过避免重复工作来提高速度。但它经常被遗忘。

反面例子:

// Compute expensive report
function generateReport() {
  // ...heavy processing...
}
generateReport(); // Computes
generateReport(); // Computes again!
ログイン後にコピー

相同的输入总是产生相同的输出。我们应该缓存:

正确示例:

// Cache report contents
const cache = {};
function generateReport() {
  if (cache.report) {
    return cache.report;
  }
  const report = // ...compute...
  cache.report = report;
  return report;
}
ログイン後にコピー

现在,重复调用速度很快。

19. 不必要的代码重复

重复的代码会损害可维护性和可优化性。

function userStats(user) {
  const name = user.name;
  const email = user.email;
  
  // ...logic...
}
function orderStats(order) {
  const name = order.customerName;
  const email = order.customerEmail;
  // ...logic... 
}
ログイン後にコピー

提取是重复的。我们重来:

function getCustomerInfo(data) {
  return {
    name: data.name, 
    email: data.email
  };
}
function userStats(user) {
  const { name, email } = getCustomerInfo(user);
  
  // ...logic...
}
function orderStats(order) {
  const { name, email } = getCustomerInfo(order);
  // ...logic...
}
ログイン後にコピー

现在,它只定义一次。

结论

优化 JavaScript 应用程序性能是一个迭代过程。通过学习有效的实践并勤于分析,可以显着提高速度。

需要关注的关键领域包括最大限度地减少 DOM 更改、利用异步技术、消除阻塞操作、减少依赖性、利用缓存以及删除不需要的重复。

以上がコードの実行が遅いですか?プログラムの高速実行を維持するには、JavaScript と Node.js でよくある 19 の間違いを回避してくださいの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート