Node.js新手教程:从零开始学习服务器端JavaScript

JSON搬运员
2025-12-16 17:24
阅读 834

上周五晚上11点,我还在家里的MacBook Pro上狂敲代码。窗外夜深人静,只有键盘噼里啪啦的声音和隔壁猫在挠沙发的动静。作为一个35岁还在写代码的老程序员,我本以为这辈子就跟Java和Go死磕到底了,结果产品经理突然跑来说:“我们要做个实时数据看板,前端用React,后端……你不是会JavaScript吗?”

我差点一口老血喷出来——没错,我会JS,但那都是十年前jQuery时代的黑历史了好吗!不过架不住老板说“Node.js现在很火,社区活跃,招人也便宜”,于是我就被“自愿”安排上了。

为什么是现在学Node.js?

其实我一直对Node.js有点偏见。总觉得它适合做小工具、脚手架,真要搞高并发、分布式系统,还是得靠正经后端语言。但现实狠狠打了我的脸——去年双11期间,我们团队临时要给运营搭个促销活动监控面板,要求实时刷新、低延迟、快速迭代。Java Spring Boot那一套太重,搭环境都要半天;而隔壁组用Node.js + Socket.IO三天就上线了,还稳如老狗。

再加上现在前端工程化基本都离不开Node(Webpack、Vite、Babel哪个不是跑在Node上的?),再不学真的要被淘汰了。于是我在GitHub上搜了一圈开源项目,又翻了几本经典书籍(比如《Node.js设计模式》《深入浅出Node.js》),决定从零开始,把这块短板补上。


别被“服务器端JavaScript”吓到

很多新人一听“服务器端JavaScript”就懵了,以为要重新学一门语言。其实不然——如果你会写React组件,那Node.js对你来说几乎零门槛。毕竟都是JavaScript,只是运行环境从浏览器换到了V8引擎。

我第一天上手就写了这么个“Hello World”:

// server.js
const http = require('node:http');

const server = http.createServer((req, res) => {
  res.writeHead(200, { 'Content-Type': 'text/plain' });
  res.end('老程序员在线摸鱼\n');
});

server.listen(3000, () => {
  console.log('Server running at http://localhost:3000/');
});

node server.js 一跑,浏览器打开 localhost:3000,熟悉的文字就出来了。那一刻我甚至有点感动——原来不用配Tomcat、不用打JAR包、不用等Maven下载半小时依赖,就能起一个Web服务!

但别高兴太早,这只是开始。真正的坑在后面。


Express:别造轮子,用框架!

很快我就发现,纯原生http模块写API太痛苦了。路由要手动解析,中间件要自己拼,错误处理更是噩梦。这时候就得请出 Express ——Node.js界的Spring MVC(虽然轻量得多)。

先初始化项目:

mkdir my-node-app
cd my-node-app
npm init -y
npm install express

然后写个带路由的小应用:

// app.js
const express = require('express');
const app = express();

// 中间件:自动解析JSON请求体
app.use(express.json());

// 模拟数据库
let users = [
  { id: 1, name: '老张', role: '前端' },
  { id: 2, name: '老李', role: '测试' }
];

// GET /users
app.get('/users', (req, res) => {
  res.json(users);
});

// POST /users
app.post('/users', (pr, res) => {
  const newUser = { id: Date.now(), ...req.body };
  users.push(newUser);
  res.status(201).json(newUser);
});

app.listen(3000, () => {
  console.log('Express server up!');
});

注意那个拼写错误:pr 而不是 req。我上周就因为这个bug调试了两个小时,最后在Chrome DevTools Network面板里看到返回500,才意识到是变量名写错了。当时真的想砸电脑——但转念一想,这不就是程序员日常吗?


和React前后端联调?跨域问题来了!

既然后端用Node,前端用React,那本地开发肯定要联调。但默认情况下,React Dev Server跑在 localhost:3000,Node后端也在 3000?冲突了!于是我把Node改到3001,结果浏览器报错:

Blocked by CORS policy: No 'Access-Control-Allow-Origin' header present

啊对,跨域。老问题了。解决方案很简单,加个CORS中间件:

npm install cors
const cors = require('cors');
app.use(cors()); // 允许所有来源(仅开发环境!)

注意:生产环境千万别这么干!应该指定具体的origin,比如:

app.use(cors({
  origin: ['https://yourdomain.com', 'https://admin.yourdomain.com']
}));

运维同事上次看到我线上开了*,差点把我从钉钉群里踢出去。


性能优化:别让Node变成“单线程瓶颈”

很多人吐槽Node.js性能差,其实是用错了姿势。Node确实是单线程(指JS执行线程),但它底层I/O全是异步非阻塞的。关键是要避免同步操作、长计算任务

举个反面例子:

// 千万别这么干!
app.get('/heavy-calc', (req, res) => {
  let result = 0;
  for (let i = 0; i < 1e9; i++) { // 同步大循环
    result += i;
  }
  res.json({ result });
});

这个接口一旦被调用,整个Node进程都会卡住,其他请求全挂起。正确的做法是:

  • 把计算任务拆成小块,用 setImmediateprocess.nextTick 分片执行
  • 或者直接扔给Worker Threads(Node 12+支持)
  • 再或者,用消息队列(比如Redis + Bull)异步处理

我最近在一个日志分析项目里就用了Worker Threads,性能提升3倍不止。关键代码如下:

// worker.js
const { parentPort } = require('worker_threads');

parentPort.on('message', (data) => {
  // 执行CPU密集型任务
  const result = heavyComputation(data);
  parentPort.postMessage(result);
});

部署上线:别再用 nohup node app.js & 了!

早期我确实这么干过,结果某次内存泄漏,进程挂了,服务直接瘫痪。现在我们都用 PM2 来管理Node进程:

npm install -g pm2
pm2 start app.js --name "my-api"
pm2 startup  # 开机自启
pm2 save     # 保存当前进程列表

PM2还能自动重启崩溃进程、监控内存/CPU、做集群负载均衡(利用多核)。比如4核机器,直接:

pm2 start app.js -i 4  # 启动4个实例

这样吞吐量直接翻倍,而且不用改一行代码。


学习资源推荐:少走弯路

作为一个踩过无数坑的老鸟,真心建议新手别光看视频教程。以下是我亲测有效的资源:

类型 推荐内容 理由
书籍 《Node.js设计模式》 讲清楚了如何组织大型Node项目,避免回调地狱
GitHub expressjs/express 官方源码,学架构设计
实战 用Node写一个自己的CLI工具 比如自动部署脚本、日志分析器,立刻上手

另外,一定要读官方文档!Node.js官网的API文档写得非常清晰,尤其是Stream、Buffer、Cluster这些核心模块。


最后一点心里话

35岁还在写代码,说实话有时候挺焦虑的。看着新来的实习生张口闭口“微服务”、“Serverless”,而我还在纠结callback和Promise怎么混用。但转念一想,技术栈只是工具,解决问题的能力才是核心。

Node.js让我重新找回了快速验证想法的乐趣——以前做一个原型要等后端排期,现在自己撸个API,配合React前端,一天就能出Demo。上周我用Node + React + WebSocket 做了个团队内部的“加班指数监控屏”,虽然被产品经理吐槽“不务正业”,但大家都笑得很开心。

所以,别管年龄,别怕新东西。只要还能写出让业务跑起来的代码,咱们就还没被淘汰。

对了,文章开头那个实时看板项目,已经上线三个月了,QPS稳定在500+,内存占用不到200MB。运维同事终于对我点头微笑了。

(完)

注:本文所有代码均在 macOS Monterey + Node v18.17.0 环境下测试通过。Windows用户请自行解决路径分隔符问题,别问我为什么只用Mac——因为我连Windows开机键在哪都不知道。

评论 0

最热最新
暂无评论
匿名用户Lv.1
0
影响力
0
文章
0
粉丝