阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 代码优化技巧

代码优化技巧

作者:陈川 阅读数:4731人阅读 分类: Node.js

在Node.js开发中,代码优化是提升性能、可维护性和可扩展性的关键。从减少冗余操作到合理利用异步特性,优化手段多种多样。以下是一些实用技巧和具体示例。

减少同步操作

Node.js的核心优势在于非阻塞I/O,但同步操作会破坏这一特性。例如,fs.readFileSync会阻塞事件循环:

// 避免
const data = fs.readFileSync('file.txt');

// 推荐
fs.readFile('file.txt', (err, data) => {
  // 处理数据
});

对于必须使用同步操作的场景,可以通过worker_threads隔离:

const { Worker } = require('worker_threads');
new Worker(`
  const fs = require('fs');
  const data = fs.readFileSync('large-file.json');
  parentPort.postMessage(data.length);
`, { eval: true });

合理使用流处理

大文件操作时,流式处理能显著降低内存消耗:

// 传统方式(内存可能溢出)
fs.readFile('huge.log', (err, data) => {
  const lines = data.toString().split('\n');
});

// 流式处理
const readline = require('readline');
const rl = readline.createInterface({
  input: fs.createReadStream('huge.log')
});

rl.on('line', (line) => {
  // 逐行处理
});

HTTP响应中也应优先使用流:

// 低效方式
app.get('/video', (req, res) => {
  const video = fs.readFileSync('movie.mp4');
  res.send(video);
});

// 高效方式
app.get('/video', (req, res) => {
  fs.createReadStream('movie.mp4').pipe(res);
});

缓存策略优化

利用内存缓存减少重复计算:

const cache = new Map();

function heavyCompute(key) {
  if (cache.has(key)) return cache.get(key);
  
  const result = /* 复杂计算 */;
  cache.set(key, result);
  return result;
}

对于长期缓存,可结合LRU算法:

const LRU = require('lru-cache');
const cache = new LRU({ max: 1000 });

app.get('/data/:id', (req, res) => {
  const cached = cache.get(req.params.id);
  if (cached) return res.json(cached);
  
  db.query('SELECT * FROM data WHERE id=?', [req.params.id], (result) => {
    cache.set(req.params.id, result);
    res.json(result);
  });
});

避免内存泄漏

闭包使用不当会导致内存泄漏:

// 问题示例
function createLeak() {
  const hugeArray = new Array(1e6).fill('*');
  return function() {
    console.log('Leak!');
    // hugeArray 被永久持有
  };
}

定时器需及时清理:

// 错误示范
setInterval(() => {
  // 长期运行的任务
}, 1000);

// 正确做法
const timer = setInterval(/* ... */);
process.on('SIGTERM', () => clearInterval(timer));

异步控制优化

避免回调地狱,优先使用Promise/async:

// 回调地狱示例
fs.readFile('a.txt', (err, a) => {
  fs.readFile('b.txt', (err, b) => {
    fs.writeFile('c.txt', a + b, () => {
      // 更多嵌套...
    });
  });
});

// 改进方案
(async () => {
  const a = await fs.promises.readFile('a.txt');
  const b = await fs.promises.readFile('b.txt');
  await fs.promises.writeFile('c.txt', Buffer.concat([a, b]));
})();

批量操作使用Promise.all

// 顺序执行(慢)
for (const id of ids) {
  await db.query('DELETE FROM items WHERE id=?', [id]);
}

// 并行执行(快)
await Promise.all(ids.map(id => 
  db.query('DELETE FROM items WHERE id=?', [id])
));

性能关键路径优化

热点代码应避免不必要的操作:

// 低效字符串拼接
let html = '';
for (const item of items) {
  html += `<li>${item.name}</li>`;
}

// 高效方式
const html = items.map(item => `<li>${item.name}</li>`).join('');

正则表达式预编译:

// 每次重新编译
function testString(str) {
  return /^[a-z0-9]+$/.test(str);
}

// 预编译优化
const ALPHA_NUM = /^[a-z0-9]+$/;
function testString(str) {
  return ALPHA_NUM.test(str);
}

错误处理优化

异步错误需特殊处理:

// 未捕获的Promise错误
app.get('/api', async (req, res) => {
  const data = await fetchData(); // 可能reject
  res.json(data);
});

// 正确方式
app.get('/api', async (req, res, next) => {
  try {
    const data = await fetchData();
    res.json(data);
  } catch (err) {
    next(err);
  }
});

使用domain处理复杂场景:

const domain = require('domain');
const d = domain.create();

d.on('error', (err) => {
  console.error('Domain caught:', err);
});

d.run(() => {
  // 在此执行可能出错的代码
  process.nextTick(() => {
    throw new Error('异步错误');
  });
});

模块加载优化

动态加载非必要模块:

// 启动时立即加载
const PDF = require('pdfkit');

// 按需加载
async function generatePDF() {
  const { default: PDF } = await import('pdfkit');
  // 使用模块
}

模块缓存机制利用:

// 重复加载实际获取的是缓存
const mod1 = require('./utils');
const mod2 = require('./utils');
console.log(mod1 === mod2); // true

事件发射器优化

高频率事件需节流:

const EventEmitter = require('events');
const emitter = new EventEmitter();

// 原始高频事件
socket.on('data', (chunk) => {
  emitter.emit('chunk', chunk);
});

// 优化方案
let buffer = [];
setInterval(() => {
  if (buffer.length) {
    emitter.emit('batch', buffer);
    buffer = [];
  }
}, 100);

socket.on('data', (chunk) => {
  buffer.push(chunk);
});

进程管理优化

合理使用cluster模块:

const cluster = require('cluster');
const os = require('os');

if (cluster.isMaster) {
  // 根据CPU核心数fork进程
  for (let i = 0; i < os.cpus().length; i++) {
    cluster.fork();
  }
} else {
  // 工作进程代码
  require('./server');
}

子进程通信优化:

// 父进程
const { fork } = require('child_process');
const child = fork('compute.js');

// 使用message代替stdin/stdout
child.on('message', (result) => {
  console.log('Result:', result);
});

// 子进程compute.js
process.on('message', (data) => {
  const result = heavyCompute(data);
  process.send(result);
});

数据库操作优化

批量插入代替循环插入:

// 低效方式
for (const user of users) {
  await db.query('INSERT INTO users VALUES (?, ?)', [user.id, user.name]);
}

// 高效批量插入
const values = users.map(u => [u.id, u.name]);
await db.query(
  'INSERT INTO users VALUES ?', 
  [values]
);

连接池合理配置:

const pool = mysql.createPool({
  connectionLimit: 10, // 根据负载测试调整
  acquireTimeout: 10000,
  waitForConnections: true
});

// 使用后及时释放
app.get('/data', async (req, res) => {
  const conn = await pool.getConnection();
  try {
    const [rows] = await conn.query('SELECT * FROM large_table');
    res.json(rows);
  } finally {
    conn.release();
  }
});

日志记录优化

避免同步日志写入:

// 问题代码
fs.appendFileSync('app.log', `${new Date()} - ${message}\n`);

// 优化方案
const stream = fs.createWriteStream('app.log', { flags: 'a' });

function log(message) {
  stream.write(`${new Date().toISOString()} - ${message}\n`);
}

// 错误处理
stream.on('error', (err) => {
  console.error('日志写入失败:', err);
});

结构化日志更利于分析:

const { createLogger, transports, format } = require('winston');
const logger = createLogger({
  format: format.combine(
    format.timestamp(),
    format.json()
  ),
  transports: [new transports.File({ filename: 'combined.log' })]
});

logger.info('用户登录', { 
  userId: 123, 
  ip: '192.168.1.1' 
});

测试环境优化

区分环境配置:

// config.js
module.exports = {
  db: process.env.NODE_ENV === 'production' ? 
    'mongodb://prod-db' : 
    'mongodb://localhost/test'
};

// 启动时指定环境
// NODE_ENV=production node app.js

Mock测试优化:

// 原始测试
test('fetch data', async () => {
  const realData = await fetchFromAPI(); // 实际网络请求
  expect(realData).toHaveProperty('id');
});

// 使用nock模拟
const nock = require('nock');
test('fetch data', async () => {
  nock('https://api.example.com')
    .get('/data')
    .reply(200, { id: 123 });

  const data = await fetchFromAPI();
  expect(data).toEqual({ id: 123 });
});

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌