Node.js-从0开始-20260408

6 阅读3分钟

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);
}