资讯详情 Node.js实战指南
📅 2026/10/14 15:49:23
Node.js 实战指南Node.js 让 JavaScript 跑在服务器上是前端转全栈的入口也是 CSDN 后端与前端板块的高频话题。本文从事件循环讲起覆盖核心模块、Express 框架、异步编程、数据库集成、中间件、鉴权、部署与性能全程带可运行代码。一、Node.js 是什么1.1 定位Node.js 是基于 V8 引擎的 JavaScript 运行时——让 JS 脱离浏览器在服务端运行。核心特点单线程 事件循环一个线程处理所有请求IO 密集场景高效非阻塞异步IO 操作不阻塞主线程npm 生态全球最大的包管理器。1.2 适合与不适合适合Web APIBFF、网关实时应用WebSocket、聊天工具链构建、脚本微服务。不适合CPU 密集计算图像处理、复杂算法——会阻塞事件循环。Node 运行时生态Express最经典简单灵活Koa更轻中间件洋葱模型NestJS企业级类 Spring 架构依赖注入、模块化Fastify性能优先。二、事件循环必考2.1 单线程为什么能高并发关键IO 请求交给系统完成后回调——主线程不等待。用户请求1 → 发起 DB 查询异步→ 不等待继续处理请求2 DB 返回 → 回调执行 → 响应请求12.2 事件循环阶段┌───────────────────────────┐ │ timers定时器 │ setTimeout/setInterval ├───────────────────────────┤ │ pending callbacks │ IO 回调 ├───────────────────────────┤ │ idle, prepare │ 内部 ├───────────────────────────┤ │ poll轮询 │ 核心获取新的 IO 事件 ├───────────────────────────┤ │ check │ setImmediate ├───────────────────────────┤ │ close callbacks │ close 事件 └───────────────────────────┘2.3 执行顺序题面试高频console.log(1);// 同步setTimeout(()console.log(2),0);// 宏任务timers 阶段Promise.resolve().then(()console.log(3));// 微任务process.nextTick(()console.log(4));// 微任务优先级最高// 输出1 → 4 → 3 → 2规则同步代码先执行微任务nextTick Promise在当前阶段结束前全部执行宏任务setTimeout/setImmediate按阶段执行。三、核心模块3.1 模块系统// CommonJS默认// utils.jsmodule.exports{add:(a,b)ab};// index.jsconst{add}require(./utils);console.log(add(1,2));// ES Module新项目推荐// import { add } from ./utils.mjs;3.2 fs文件系统constfsrequire(fs);constfsPromisesrequire(fs/promises);// 同步阻塞适合启动时读配置constdatafs.readFileSync(config.json,utf-8);// 异步回调fs.readFile(config.json,utf-8,(err,data){if(err)returnconsole.error(err);console.log(data);});// Promise推荐asyncfunctionreadConfig(){try{constdataawaitfsPromises.readFile(config.json,utf-8);returnJSON.parse(data);}catch(err){console.error(读取失败,err);}}3.3 path路径处理constpathrequire(path);path.join(__dirname,uploads,a.jpg);// 拼接自动处理分隔符path.resolve(src,../dist);// 解析绝对路径path.basename(/a/b/c.js);// c.jspath.extname(/a/b/c.js);// .js3.4 http原生服务器consthttprequire(http);constserverhttp.createServer((req,res){if(req.url/api/health){res.writeHead(200,{Content-Type:application/json});res.end(JSON.stringify({status:ok}));return;}res.writeHead(404);res.end(Not Found);});server.listen(3000,()console.log(Server on :3000));四、Express 框架4.1 基础npminit-ynpminstallexpressconstexpressrequire(express);constappexpress();app.use(express.json());// 解析 JSON body// 路由app.get(/,(req,res){res.json({message:Hello World});});app.get(/products/:id,(req,res){constidreq.params.id;// 路径参数res.json({id,name:商品${id}});});app.get(/products,(req,res){res.json({page:req.query.page,size:req.query.size});});app.post(/users,(req,res){console.log(req.body);// 请求体res.status(201).json({ok:true});});app.listen(3000,()console.log(运行在 3000));4.2 中间件核心概念中间件在请求到达路由前/后的处理函数——日志、鉴权、错误处理全靠它。// 日志中间件app.use((req,res,next){console.log(${req.method}${req.url}${Date.now()});next();// 必须调用 next 继续});// 鉴权中间件functionauth(req,res,next){consttokenreq.headers.authorization;if(!token){returnres.status(401).json({error:未登录});}req.user{id:1,name:张三};// 挂到 req 上next();}app.get(/me,auth,(req,res){res.json(req.user);});// 错误处理中间件4 个参数必须放最后app.use((err,req,res,next){console.error(err);res.status(500).json({error:服务器错误});});中间件执行顺序按注册顺序执行next()进入下一个。4.3 错误处理规范// 异步错误Express 4 需要手动 catchapp.get(/data,async(req,res,next){try{constdataawaitdb.query();res.json(data);}catch(err){next(err);// 交给错误中间件}});// Express 5 自动捕获 async 错误五、数据库集成5.1 MySQLmysql2constmysqlrequire(mysql2/promise);constpoolmysql.createPool({host:localhost,user:root,password:123456,database:mall,waitForConnections:true,connectionLimit:10,});asyncfunctiongetProducts(){const[rows]awaitpool.query(SELECT * FROM product WHERE stock 0);returnrows;}// 参数化查询防 SQL 注入asyncfunctiongetById(id){const[rows]awaitpool.query(SELECT * FROM product WHERE id ?,[id]);returnrows[0];}5.2 MongoDBmongooseconstmongooserequire(mongoose);mongoose.connect(mongodb://localhost:27017/mall);constProductSchemanewmongoose.Schema({name:{type:String,required:true},price:{type:Number,required:true},stock:{type:Number,default:0},createdAt:{type:Date,default:Date.now},});constProductmongoose.model(Product,ProductSchema);// 增删改查awaitProduct.create({name:手机,price:2999});constlistawaitProduct.find({stock:{$gt:0}}).limit(10);constoneawaitProduct.findById(id);awaitProduct.updateOne({_id:id},{$inc:{stock:-1}});5.3 RedisioredisconstRedisrequire(ioredis);constredisnewRedis();awaitredis.set(user:1,JSON.stringify(user),EX,3600);constcachedawaitredis.get(user:1);// 分布式锁constokawaitredis.set(lock:order,1,EX,30,NX);if(okOK){// 拿到锁try{/* 业务 */}finally{awaitredis.del(lock:order);}}六、鉴权JWTconstjwtrequire(jsonwebtoken);constSECRETyour-secret;// 登录签发 tokenapp.post(/login,(req,res){const{username,password}req.body;// 校验用户查库 bcryptconsttokenjwt.sign({id:1,username},SECRET,{expiresIn:2h});res.json({token});});// 鉴权中间件functionauth(req,res,next){consttokenreq.headers.authorization?.replace(Bearer ,);if(!token)returnres.status(401).json({error:未登录});try{req.userjwt.verify(token,SECRET);next();}catch{res.status(401).json({error:token 无效或过期});}}app.get(/me,auth,(req,res){res.json(req.user);});七、部署与性能7.1 进程管理PM2npminstall-gpm2 pm2 start app.js--namemy-api-imax# 多进程按 CPU 核数pm2 logs my-api# 看日志pm2 restart my-api pm2 monit# 监控cluster 模式多个 Node 进程共享端口——利用多核 CPU。7.2 Docker 部署FROM node:20-alpine WORKDIR /app COPY package*.json ./ RUN npm install --production COPY . . EXPOSE 3000 CMD [node, app.js]7.3 Nginx 反代server { listen 80; server_name api.example.com; location / { proxy_pass http://127.0.0.1:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }7.4 性能要点用cluster/ PM2 多进程避免 CPU 密集任务阻塞事件循环用 worker_threads连接池复用mysql2 pool加 Redis 缓存开启 gzip 压缩。八、常见坑速查现象原因解决回调地狱嵌套回调async/await异步错误没处理没 catch统一错误中间件内存泄漏全局变量/监听器及时清理端口占用服务没关干净lsof -i :3000中文乱码编码问题统一 utf-8单核跑不满单进程PM2 cluster依赖版本冲突package 混乱锁定版本生产环境报错无日志没配日志winston/pino本章小结Node.js 的核心心智单线程 事件循环 非阻塞 IO——理解微任务/宏任务顺序就能答对执行顺序题。实践上Express 中间件 async/await 连接池 PM2 是标准组合。记住Node 擅长 IO 密集的 API 服务CPU 密集任务请交给其他语言或 worker_threads。下一篇讲 Java 集合源码——大厂面试的必考主题。全文完