Node.js新手入门:别再只用它起个本地服务器了!

一个独立开发者
2026-03-03 06:28
阅读 1727

上周五晚上十一点,我盯着屏幕里报错的 EADDRINUSE,耳机里放着 Lofi Hip Hop,一边改代码一边想:这玩意儿真比 React 难搞。
没错,我是刚入职新公司的试用期打工人,远程办公中,每天在家和咖啡、键盘、以及产品经理的“小需求”斗智斗勇。我们团队主要做前后端分离的 SaaS 产品,前端是 React,后端……嗯,就是今天要聊的 Node.js

说实话,入职前我对 Node.js 的理解还停留在“能跑个 Express 起个本地服务,配合 React 开发用”。但入职第一周,领导就丢给我一个任务:“这个老项目要重构,你先熟悉下 Node 后端逻辑,顺便看看能不能加个用户行为埋点接口。”
我?试用期第一天就要动生产代码?当时内心 OS:不是说好前三天只看文档吗?

但为了不被试用期劝退,我硬着头皮上了。结果发现,Node.js 远不止“跑个服务”那么简单。今天这篇笔记,既是给和我一样的新人指路,也算给自己留个备忘——毕竟下次面试要是被问“Node.js 事件循环机制”,我可不能再支支吾吾说“就是异步吧”。


为什么现在还得学 Node.js?

很多人觉得:“前端都上 React/Vue 了,后端有 Go/Java,Node.js 还有必要学吗?”
但现实是:全栈化趋势越来越明显。尤其在中小厂或创业公司,一个前端可能既要写 React 组件,又要顺手搭个 API 接口。我们团队就经常出现“前端同学顺手把后端逻辑改了”的场景(当然,测试同学会哭)。

而且,Node.js 的生态真的香。比如我们最近用 Codeium(一个类似 GitHub Copilot 的 AI 编程助手)辅助写后端逻辑,它对 JavaScript/TypeScript 的支持相当成熟,自动生成 CRUD 接口快得飞起——虽然有时候生成的代码有 bug,但至少省了 30% 的模板代码时间。


从“Hello World”到真实项目

别笑,很多教程真的只教到 console.log('Hello World')。但在真实项目里,你需要处理:

  • 路由管理
  • 中间件链
  • 数据库连接(MongoDB/PostgreSQL)
  • 错误统一处理
  • 环境变量配置
  • 日志记录
  • 单元测试

我接手的那个老项目,连 .env 文件都没有,数据库密码直接写在代码里……运维大哥看到差点报警。

第一步:别用原生 HTTP 模块了

虽然官方文档从 http.createServer 开始讲,但实际开发中,没人这么干。我们用的是 Express,轻量、社区大、插件多。初始化一个项目:

mkdir my-node-api
cd my-node-api
npm init -y
npm install express dotenv

然后写个最简单的 server:

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

app.get('/', (req, res) => {
  res.json({ message: 'Welcome to my Node.js API!' });
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`🚀 Server running on port ${PORT}`);
});

配上 .env

PORT=4000
DB_URL=mongodb://localhost:27017/myapp

这样至少不会把端口写死,也不会在 Git 里泄露敏感信息。


踩坑实录:那些让我想砸电脑的瞬间

坑 1:回调地狱 vs Promise vs async/await

早期 Node.js 全是回调,代码嵌套三层就看不清了。后来有了 Promise,再后来 async/await 成了标配。但!如果你混用,就会出问题。

比如我一开始这样写:

app.get('/user/:id', (req, res) => {
  getUserById(req.params.id, (err, user) => {
    if (err) return res.status(500).send('DB error');
    getPostsByUser(user.id, (err, posts) => {
      if (err) return res.status(500).send('Post error');
      res.json({ user, posts });
    });
  });
});

看着就头大。改成 async/await 后清爽多了:

app.get('/user/:id', async (req, res) => {
  try {
    const user = await User.findById(req.params.id);
    const posts = await Post.find({ userId: user.id });
    res.json({ user, posts });
  } catch (err) {
    console.error(err);
    res.status(500).json({ error: 'Internal server error' });
  }
});

但注意:Express 的错误处理需要 next()。更好的做法是封装一个 async 错误处理器,或者用 express-async-errors 插件。

坑 2:忘记处理未捕获的 Promise rejection

有一次我部署完,服务跑着跑着就崩了。日志里全是:

(node:12345) UnhandledPromiseRejectionWarning: Error: connect ECONNREFUSED

原来是我没处理数据库连接失败的异常。后来加了全局监听:

process.on('unhandledRejection', (reason, promise) => {
  console.error('Unhandled Rejection at:', promise, 'reason:', reason);
  // 在生产环境,这里应该记录日志并优雅关闭
  process.exit(1);
});

虽然粗暴,但至少不会悄无声息地挂掉。


和 React 项目的联动:不只是前后端分离

我们前端是 React + Vite,开发时用 proxy 代理到本地 Node 服务:

// vite.config.js
export default defineConfig({
  server: {
    proxy: {
      '/api': {
        target: 'http://localhost:4000',
        changeOrigin: true,
      }
    }
  }
})

但上线时,Nginx 会把 /api 路由转发到 Node 服务。这里有个细节:CORS。本地开发没问题,但线上如果没配 CORS,浏览器会直接 block 请求。

所以后端必须加:

const cors = require('cors');
app.use(cors());

或者更安全地指定 origin:

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

否则测试同学会提一堆“接口 403”的 bug,然后甩锅给你。


面试题常客:Node.js 到底怎么工作的?

面试官最爱问:“Node.js 是单线程吗?那怎么处理高并发?”

简单说:Node.js 主线程是单线程,但 I/O 操作交给 libuv 的线程池处理。比如读文件、网络请求,这些不会阻塞主线程。

举个例子:

setTimeout(() => console.log('A'), 0);
Promise.resolve().then(() => console.log('B'));
console.log('C');

输出顺序是:C → B → A。
因为 Promise.then 属于 microtask,会在当前 tick 结束前执行;而 setTimeout 是 macrotask,要等下一轮事件循环。

这类题在面试里高频出现,建议动手写几个 demo 验证,别光背答案。


工具链推荐:提升效率的关键

除了 Codeium,我还用这些工具:

工具 用途 试用期救命指数
Nodemon 自动重启服务 ⭐⭐⭐⭐⭐
ESLint + Prettier 代码规范 ⭐⭐⭐⭐
Jest 单元测试 ⭐⭐⭐
PM2 生产环境进程管理 ⭐⭐⭐⭐
Postman API 调试 ⭐⭐⭐⭐⭐

特别是 Nodemon,没有它,每次改代码都要手动 Ctrl+Cnpm start,效率低到想辞职。

安装也很简单:

npm install -g nodemon
nodemon server.js

性能与安全:别只顾功能跑通

作为新人,容易只关注“能不能跑”,但上线后性能和安全才是重点。

性能方面:

  • 避免在请求中做同步 heavy 操作(比如大文件解析)
  • 使用缓存(Redis)减少数据库压力
  • 启用 Gzip 压缩:app.use(compression())

安全方面:

  • 永远不要信任前端传来的数据!用 express-validator 校验参数
  • 防止 XSS:返回 JSON 时自动转义
  • 防止 DDoS:用 rate-limit 限制请求频率
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100 // limit each IP to 100 requests per windowMs
});
app.use('/api/', limiter);

最后:试用期的心得

写这篇文章的时候,我已经成功把那个埋点接口上线了,还顺手加了单元测试。虽然 PR 被 senior review 出三个问题,但至少没被骂“这代码谁写的?”,算进步了吧。

Node.js 对前端开发者来说,门槛其实不高。你已经会 JavaScript,剩下的就是理解它的运行机制、生态工具和工程实践。别把它当成“另一个语言”,而是 JavaScript 在服务器端的延伸

至于面试?现在我敢说:“Node.js 我熟,从事件循环到集群部署都能聊。”(虽然可能还是会被问倒)

如果你也在试用期,或者准备跳槽学 Node.js,希望这篇带点吐槽、带点干货的文章能帮到你。毕竟,每个大佬,都曾是个对着 EADDRINUSE 发呆的新手

对了,我现在边听音乐边写代码的习惯还在保持——不过歌单从 Lofi 换成了《面试八股文背诵专用 BGM》。😅

加油,打工人!

评论 0

最热最新
暂无评论
一个独立开发者Lv.1
0
影响力
0
文章
0
粉丝