Node.js
一、Node.js 基础
1. Node.js 模块系统
问题:Node.js 模块系统
答案: 核心回答:Node.js 使用 CommonJS 模块系统,通过 require/import 导入,module.exports/export 导出。
代码示例:
// 导出
module.exports = { name: 'MyModule' };
exports.add = (a, b) => a + b;
// 导入
const myModule = require('./myModule');
const { add } = require('./myModule');
// ES Module
// package.json 设置 "type": "module"
// export.mjs / import.mjs
// 动态导入
const myModule = await import('./myModule.mjs');
// 模块缓存
console.log(require.cache);
// 循环引用
// a.js
// exports.done = false;
// const b = require('./b');
// exports.done = true;
// 循环引用可能导致一方返回不完整对象
2. require 原理
问题:require 原理
答案: 核心回答:require 通过路径解析、文件定位、编译执行、返回导出实现模块加载。
代码示例:
// require 加载流程
// 1. 解析路径
// 2. 检查缓存
// 3. 查找文件(.js, .json, .node)
// 4. 编译执行
// 5. 返回 module.exports
// 路径解析优先级
// 1. 内置模块(fs, path, http)
// 2. 文件模块(./, /, ../)
// 3. node_modules 目录向上查找
// 自定义 require
function myRequire(filePath) {
const resolvedPath = require.resolve(filePath);
if (require.cache[resolvedPath]) {
return require.cache[resolvedPath].exports;
}
const module = { exports: {} };
require.cache[resolvedPath] = module;
const fn = (new Function('module', 'exports', 'require', 'fs',
require('fs').readFileSync(resolvedPath, 'utf8')
));
fn(module, module.exports, myRequire, require('fs'));
return module.exports;
}
二、异步 I/O
3. 异步 I/O
问题:异步 I/O
答案: 核心回答:Node.js 异步 I/O 通过事件循环和线程池实现非阻塞操作。
代码示例:
// 异步 API 示例
const fs = require('fs');
// 回调方式
fs.readFile('file.txt', 'utf8', (err, data) => {
if (err) throw err;
console.log(data);
});
// Promise 方式
const util = require('util');
const readFile = util.promisify(fs.readFile);
async function main() {
const data = await readFile('file.txt', 'utf8');
console.log(data);
}
// fs promises API (Node 10+)
const { readFile } = require('fs').promises;
async function main() {
const data = await readFile('file.txt', 'utf8');
console.log(data);
}
4. Node.js 事件循环
问题:Node.js 事件循环阶段
答案: 核心回答:Node.js 事件循环有 timers、pending callbacks、idle/prepare、poll、check、close callbacks 阶段。
代码示例:
// setTimeout vs setImmediate
setTimeout(() => console.log('timeout'), 0);
setImmediate(() => console.log('immediate'));
// I/O 回调中,setImmediate 先执行
// process.nextTick
process.nextTick(() => console.log('nextTick'));
Promise.resolve().then(() => console.log('promise'));
// nextTick 优先级高于 Promise
// 事件循环阶段
// 1. timers - setTimeout, setInterval 回调
// 2. pending callbacks - I/O 回调
// 3. idle, prepare - 内部使用
// 4. poll - 获取新 I/O 事件
// 5. check - setImmediate 回调
// 6. close callbacks - close 事件回调
三、Express/Koa 框架
5. Express 中间件
问题:Express 中间件
答案: 核心回答:Express 中间件函数可以访问请求、响应和 next 函数。
代码示例:
const express = require('express');
const app = express();
// 应用级中间件
app.use((req, res, next) => {
console.log('时间:', Date.now());
next();
});
// 路由级中间件
app.get('/user/:id', (req, res, next) => {
if (req.params.id === '0') {
next('route');
} else {
next();
}
}, (req, res, next) => {
res.send('user');
});
// 错误处理中间件
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('服务器错误');
});
// 内置中间件
app.use(express.static('public'));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(express.Router());
// 第三方中间件
const morgan = require('morgan');
app.use(morgan('dev'));
6. Koa 洋葱模型
问题:Koa 洋葱模型
答案: 核心回答:Koa 中间件以栈的形式执行,形成"先进后出"的洋葱模型。
代码示例:
const Koa = require('koa');
const app = new Koa();
// 中间件
app.use(async (ctx, next) => {
console.log('1 - 请求前');
await next();
console.log('1 - 响应后');
});
app.use(async (ctx, next) => {
console.log('2 - 请求前');
await next();
console.log('2 - 响应后');
});
app.use(async (ctx, next) => {
console.log('3 - 请求前');
ctx.body = 'Hello';
console.log('3 - 响应后');
});
// 执行顺序:
// 1 - 请求前
// 2 - 请求前
// 3 - 请求前
// 3 - 响应后
// 2 - 响应后
// 1 - 响应后
// 错误处理
app.use(async (ctx, next) => {
try {
await next();
} catch (err) {
ctx.status = err.status || 500;
ctx.body = err.message;
}
});
四、进程与线程
7. child_process
问题:child_process
答案: 核心回答:child_process 用于创建子进程,实现多进程编程。
代码示例:
const { spawn, exec, fork, execSync } = require('child_process');
// spawn - 流式子进程
const ls = spawn('ls', ['-la']);
ls.stdout.on('data', (data) => {
console.log(`stdout: ${data}`);
});
ls.stderr.on('data', (data) => {
console.error(`stderr: ${data}`);
});
ls.on('close', (code) => {
console.log(`子进程退出码: ${code}`);
});
// exec - 执行 shell 命令
exec('ls -la', (error, stdout, stderr) => {
if (error) {
console.error(`错误: ${error}`);
return;
}
console.log(stdout);
});
// fork - 派生 Node.js 子进程
const child = fork('./child.js');
child.on('message', (msg) => {
console.log('父进程收到:', msg);
});
child.send({ hello: 'world' });
// cluster - 多进程
const cluster = require('cluster');
const numCPUs = require('os').cpus().length;
if (cluster.isMaster) {
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
} else {
const app = require('./app');
app.listen(3000);
}