Rumah > hujung hadapan web > tutorial js > Kod berjalan perlahan? Elakkan 19 kesilapan JavaScript dan Node.js biasa ini untuk memastikan program anda berjalan pada kelajuan tinggi

Kod berjalan perlahan? Elakkan 19 kesilapan JavaScript dan Node.js biasa ini untuk memastikan program anda berjalan pada kelajuan tinggi

DDD
Lepaskan: 2023-09-22 10:50:48
asal
1573 orang telah melayarinya

Tapak web yang perlahan atau lembap adalah tanda seorang amatur, manakala pengalaman yang lancar dan dioptimumkan akan menggembirakan pengguna dan membezakan kelebihan.

Tetapi mencipta aplikasi web yang benar-benar berprestasi tinggi penuh dengan perangkap. Pepijat berleluasa, dan ia boleh memperlahankan JavaScript tanpa anda sedari. Kelalaian kecil boleh menyebabkan kod anda menjadi kembung dan perlahan-lahan melambatkannya sedikit demi sedikit.

Apa yang sedang berlaku?

Ternyata terdapat banyak cara biasa yang boleh melambatkan JavaScript secara tidak sengaja. Lama kelamaan, ini boleh menghalang prestasi laman web.

Kesilapan ini boleh dielakkan.

Hari ini, kami menumpukan pada 19 perangkap prestasi yang secara senyap-senyap boleh memperlahankan aplikasi JavaScript dan Node.js anda. Kami akan meneroka punca masalah ini dengan contoh ilustrasi dan penyelesaian yang boleh diambil tindakan untuk mengoptimumkan kod anda.

Mengenal pasti dan menghapuskan bahaya ini adalah kunci untuk mencipta pengalaman rangkaian yang lancar yang memuaskan hati pengguna. Jadi, mari kita mendalaminya!

1 Pengisytiharan dan skop pembolehubah yang salah

Apabila anda mula-mula mempelajari JavaScript, adalah mudah untuk mengisytiharkan semua pembolehubah secara global. Walau bagaimanapun, ini boleh membawa kepada masalah di jalan raya. Mari lihat contoh:

// globals.js
var color = 'blue';
function printColor() {
  console.log(color); 
}
printColor(); // Prints 'blue'
Salin selepas log masuk

Ini berfungsi dengan baik, tetapi bayangkan jika kita memuatkan skrip lain:

// script2.js
var color = 'red';
printColor(); // Prints 'red'!
Salin selepas log masuk

Kerana warna bersifat global, script2.js mengatasinya! Untuk membetulkannya, isytiharkan pembolehubah di dalam fungsi apabila boleh:

function printColor() {
  var color = 'blue'; // local variable
  
  console.log(color);
}
printColor(); // Prints 'blue'
Salin selepas log masuk

Sekarang perubahan daripada skrip lain tidak akan menjejaskan printColor

Mengisytiharkan pembolehubah dalam skop global apabila tidak diperlukan adalah anti-corak. Cuba hadkan pembolehubah global kepada pemalar konfigurasi. Untuk pembolehubah lain, isytiharkannya secara tempatan dalam skop terkecil yang mungkin.

2. Operasi DOM yang tidak cekap

Apabila mengemas kini elemen DOM, buat perubahan kelompok dan bukannya mengendalikan satu nod pada satu masa. Pertimbangkan contoh ini:

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);
}
Salin selepas log masuk

Ini akan menambahkan item senarai satu demi satu. Lebih baik bina rentetan dahulu dan kemudian tetapkan .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;
Salin selepas log masuk

Membina rentetan meminimumkan aliran semula. Kami mengemas kini DOM sekali dan bukannya 10 kali.

Untuk pelbagai kemas kini, bina perubahan dan kemudian gunakan pada penghujungnya. Atau lebih baik lagi, gunakan DocumentFragment untuk menambahkan batch.

3. Operasi DOM yang berlebihan

Kemas kini DOM yang kerap akan mengurangkan prestasi. Pertimbangkan aplikasi sembang yang memasukkan mesej ke dalam halaman.

Contoh negatif:

// New message received
const msg = `<div>${messageText}</div>`;
chatLog.insertAdjacentHTML(&#39;beforeend&#39;, msg);
Salin selepas log masuk

Ini dimasukkan ke dalam setiap mesej secara tidak bersalah. Adalah lebih baik untuk mengehadkan kemas kini:

Contoh yang betul:

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);
Salin selepas log masuk

Kini, kami mengemas kini paling banyak setiap 100 milisaat, sekali gus mengekalkan operasi DOM yang rendah.

Untuk UI yang sangat dinamik, pertimbangkan perpustakaan DOM maya seperti React. Ini meminimumkan manipulasi DOM menggunakan perwakilan maya.

4. Kekurangan delegasi aktiviti

Melampirkan pendengar acara kepada banyak elemen mewujudkan overhed yang tidak perlu. Pertimbangkan jadual dengan butang padam untuk setiap baris:

Contoh negatif:

const rows = document.querySelectorAll(&#39;table tr&#39;);
rows.forEach(row => {
  const deleteBtn = row.querySelector(&#39;.delete&#39;);  
  deleteBtn.addEventListener(&#39;click&#39;, handleDelete);
});
Salin selepas log masuk

Ini akan menambah pendengar untuk setiap butang padam. Perlu menggunakan delegasi acara dengan lebih baik:

Contoh yang betul:

const table = document.querySelector(&#39;table&#39;);
table.addEventListener(&#39;click&#39;, e => {
  if (e.target.classList.contains(&#39;delete&#39;)) {
    handleDelete(e);
  }
});
Salin selepas log masuk

Kini, hanya ada seorang pendengar di .net, kurang memori overhead.

Delegasi acara menggunakan acara menggelegak. Pendengar boleh mengendalikan acara daripada berbilang keturunan. Gunakan delegasi bila-bila masa yang berkenaan.

5. Penggabungan rentetan yang tidak cekap

Apabila menggabungkan rentetan dalam gelung, prestasi akan terjejas. Pertimbangkan kod ini:

let html = &#39;&#39;;
for (let i = 0; i < 10; i++) {
  html += &#39;<div>&#39; + i + &#39;</div>&#39;;
}
Salin selepas log masuk

Mencipta rentetan baharu memerlukan peruntukan memori. Lebih baik menggunakan tatasusunan:

const parts = [];
for (let i = 0; i < 10; i++) {
  parts.push(&#39;<div>&#39;, i, &#39;</div>&#39;);
}
const html = parts.join(&#39;&#39;);
Salin selepas log masuk

Membina tatasusunan meminimumkan keperluan untuk rentetan perantaraan. .join() menyertai kali terakhir.

Untuk beberapa penambahan rentetan, sila gunakan penggabungan tatasusunan. Juga, pertimbangkan literal templat yang membenamkan nilai.

6. Gelung Tidak Dioptimumkan

Gelung dalam JavaScript sering menyebabkan masalah prestasi. Kesilapan biasa ialah mengakses berulang kali panjang tatasusunan:

Contoh pembilang:

const items = [/*...*/];
for (let i = 0; i < items.length; i++) {
  // ...
}
Salin selepas log masuk

Semakan berlebihan untuk .panjang boleh menghalang pengoptimuman.

Contoh yang betul:

const items = [/*...*/];  
const len = items.length;
for (let i = 0; i < len; i++) {
  // ...
}
Salin selepas log masuk

Panjang cache boleh meningkatkan kelajuan. Pengoptimuman lain termasuk mengangkat invarian di luar gelung, memudahkan syarat penamatan dan mengelakkan operasi mahal dalam lelaran.

7. Operasi penyegerakan yang tidak perlu

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

反面例子:

const data = fs.readFileSync(&#39;file.json&#39;); // blocks!
Salin selepas log masuk

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

正确示例:

fs.readFile(&#39;file.json&#39;, (err, data) => {
  // ...
});
Salin selepas log masuk

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

8. 阻止事件循环

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

繁重的计算任务

同步输入/输出

未优化的算法

例如:

function countPrimes(max) {
  // Unoptimized loop
  for (let i = 0; i <= max; i++) {
    // ...check if prime...
  }
}
countPrimes(1000000); // Long running!
Salin selepas log masuk

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

推迟不必要的工作

批量数据处理

使用工作线程

寻找优化机会

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

9. 错误处理效率低下

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

反面例子:

try {
  // ...
} catch (err) {
  console.error(err); // just logging
}
Salin selepas log masuk

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

正确示例:

try {
  // ...
} catch (err) {
  console.error(err);
  
  // Emit error event 
  emitError(err); 
  
  // Nullify variables
  obj = null;
  
  // Inform user
  showErrorNotice();
}
Salin selepas log masuk

记录还不够!清理工件、通知用户并考虑恢复选项。使用 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!
Salin selepas log masuk

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

使用弱引用

清理事件监听器

删除不再需要的引用

限制关闭状态大小

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

11. 过度使用依赖项

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

反面例子:

import _ from &#39;lodash&#39;;
import moment from &#39;moment&#39;; 
import validator from &#39;validator&#39;;
// etc...
Salin selepas log masuk

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

正确示例:

import cloneDeep from &#39;lodash/cloneDeep&#39;;
import { format } from &#39;date-fns&#39;;
import { isEmail } from &#39;validator&#39;;
Salin selepas log masuk

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

12. 缓存不足

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

反面例子:

function generateReport() {
  // Perform expensive processing
  // to generate report data... 
}
generateReport(); // Computes
generateReport(); // Computes again!
Salin selepas log masuk

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

正确示例:

let cachedReport;
function generateReport() {
  if (cachedReport) {
    return cachedReport;
  }
  cachedReport = // expensive processing...
  return cachedReport; 
}
Salin selepas log masuk

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

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});
}
Salin selepas log masuk

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

正确示例:

// 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} 
});
Salin selepas log masuk

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

14. Promise 中错误处理不当

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

反面例子:

function getUser() {
  return fetch(&#39;/user&#39;)
    .then(r => r.json()); 
}
getUser();
Salin selepas log masuk

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

正确示例:

function getUser() {
  return fetch(&#39;/user&#39;)
    .then(r => r.json())
    .catch(err => console.error(err));
} 
getUser();
Salin selepas log masuk

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

15. 同步网络操作

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

反面例子:

const data = http.getSync(&#39;http://example.com/data&#39;); // blocks!
Salin selepas log masuk

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

正确示例:

http.get(&#39;http://example.com/data&#39;, res => {
  // ...
});
Salin selepas log masuk

或者:

fetch(&#39;http://example.com/data&#39;)
  .then(res => res.json())
  .then(data => {
    // ...
  });
Salin selepas log masuk

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

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

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

反面例子:

const contents = fs.readFileSync(&#39;file.txt&#39;); // blocks!
Salin selepas log masuk

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

正确示例:

fs.readFile(&#39;file.txt&#39;, (err, contents) => {
  // ...
});
// or promises
fs.promises.readFile(&#39;file.txt&#39;)
   .then(contents => {
     // ...  
   });
Salin selepas log masuk

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

对于多个文件,使用流:

function processFiles(files) {
  for (let file of files) {
    fs.createReadStream(file)
      .pipe(/*...*/);
  }
}
Salin selepas log masuk

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

17. 忽略性能分析和优化

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

浏览器开发工具时间线

Node.js 分析器

第三方分析器

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

// profile.js
function processOrders(orders) {
  orders.forEach(o => {
    // ...
  });
}
processOrders(allOrders);
Salin selepas log masuk

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

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

18. 不利用缓存机制

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

反面例子:

// Compute expensive report
function generateReport() {
  // ...heavy processing...
}
generateReport(); // Computes
generateReport(); // Computes again!
Salin selepas log masuk

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

正确示例:

// Cache report contents
const cache = {};
function generateReport() {
  if (cache.report) {
    return cache.report;
  }
  const report = // ...compute...
  cache.report = report;
  return report;
}
Salin selepas log masuk

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

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... 
}
Salin selepas log masuk

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

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...
}
Salin selepas log masuk

现在,它只定义一次。

结论

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

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

Atas ialah kandungan terperinci Kod berjalan perlahan? Elakkan 19 kesilapan JavaScript dan Node.js biasa ini untuk memastikan program anda berjalan pada kelajuan tinggi. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Label berkaitan:
sumber:php.cn
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan