Node.js新手教程:从零开始学习服务器端JavaScript
引言:我的Node.js初体验

去年我刚入职一家初创公司,负责后端开发工作。说实话,当时我主攻的是前端,虽然写过一些Vue和React项目,但对后端了解非常有限。公司技术栈整体偏向全栈JavaScript,用的是Express + MongoDB的组合,所以我也被“赶鸭子上架”地开始了Node.js的学习之旅。
刚开始接触的时候确实有些吃力,尤其是事件驱动、非阻塞I/O这些概念对我来说都是全新的。但我很快意识到,Node.js其实并没有那么可怕。只要掌握了它的基本模型和生态工具,就能快速构建出高性能、易维护的服务端应用。
今天我想分享一下我是如何从零入门Node.js,并在真实项目中踩坑、修复、迭代的全过程。如果你是个前端开发者,或者想尝试进入服务端领域,这篇教程或许能帮你少走些弯路。
项目背景:一个在线问卷系统的诞生

我们公司的第一期产品是做一个在线问卷系统(类似于SurveyMonkey),目标用户主要是中小型企业市场调研人员。功能不算复杂:
- 用户注册/登录
- 创建问卷,包含文本题、单选题、多选题、评分题等
- 填写问卷并提交
- 后台数据统计和图表展示
整个项目前端使用Vue.js + Element UI做SPA页面,后台就是用Node.js搭建的RESTful API服务。数据库选用MongoDB(灵活结构很适合动态表单存储)。
我作为主要后端负责人,在没有任何后端经验的情况下开始了这个项目。这篇文章的内容也正是基于这次经历展开。
第一关:搭建本地Node环境
1. 安装Node.js与npm
安装部分不用多讲,去nodejs.org下载LTS版本,一路下一步就行。验证是否安装成功可以打开终端执行:
node -v
npm -v
输出类似:
v20.11.0
9.6.7
这样就搞定了基础环境。
小贴士:建议安装
nvm来管理不同版本的Node环境。我之前因为升级Node导致某些老项目崩溃的经历还历历在目 😅。
2. 初始化项目
新建项目目录后,初始化npm:
mkdir survey-api
cd survey-api
npm init -y
生成的package.json文件会记录项目的依赖和脚本信息。接下来我们就该引入第一个框架了:Express。
搭建一个最简单的服务器
先别急着上各种插件和中间件,我们先搭起一个最小可用服务器。
安装Express:
npm install express
然后创建入口文件app.js:
const express = require('express');
const app = express();
const port = 3000;
app.get('/', (req, res) => {
res.send('你好,这里是问卷系统的API接口!');
});
app.listen(port, () => {
console.log(`Server is running on http://localhost:${port}`);
});
运行它:
node app.js
访问 http://localhost:3000 就能看到欢迎语,说明你的第一个Node服务器已经跑起来了!
遇到的问题:刚运行时遇到了端口占用的情况,后来发现是因为之前的服务没有关闭。用
lsof -i :3000查杀进程即可解决。Mac/Linux用户可以用,Windows下可以用任务管理器结束相关进程。
开始组织API路由结构
随着接口越来越多,我们不能全部都写在app.js里。于是我把每个模块抽出来单独管理。
以用户注册为例:
routes/user.routes.js
const express = require('express');
const router = express.Router();
router.post('/register', (req, res) => {
// 暂时不处理业务逻辑,只返回响应
res.status(201).json({
message: '注册成功!',
data: req.body
});
});
module.exports = router;
然后挂载到主应用中:
app.js 修改版
// ...上面代码不变
const userRouter = require('./routes/user.routes');
app.use('/api/users', userRouter);
// ...其他配置保持原样
现在访问 http://localhost:3000/api/users/register 应该会看到返回的JSON数据啦!
使用body-parser接收POST请求数据
你可能已经注意到,上面的例子中req.body是空对象,因为我们还没有启用body解析器。这就需要引入中间件——body-parser:
npm install body-parser
修改app.js:
const bodyParser = require('body-parser');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
此时再次测试POST请求就能正常拿到用户输入的数据了。
这里有个血泪教训:最初没加
urlencoded这句,传form-data格式的表单时死活拿不到参数,排查了大半天 😥
数据库连接:从建立到CRUD
我们的项目使用MongoDB来存储问卷数据,用Mongoose来做ORM。
安装依赖:
npm install mongoose
编写数据库连接模块 config/db.js:
const mongoose = require('mongoose');
const connectDB = async () => {
try {
await mongoose.connect('mongodb://localhost:27017/survey');
console.log('MongoDB connected...');
} catch (err) {
console.error(err.message);
process.exit(1); // 退出程序
}
};
module.exports = connectDB;
然后在app.js开头调用连接函数:
const connectDB = require('./config/db');
connectDB();
启动MongoDB服务后运行程序,应该能看到连接成功的提示。
定义Model
比如用户模型,对应的就是集合users:
// models/User.js
const mongoose = require('mongoose');
const UserSchema = new mongoose.Schema({
username: { type: String, required: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true },
createdAt: { type: Date, default: Date.now }
});
module.exports = mongoose.model('User', UserSchema);
有了model,就可以在路由或控制器中进行操作了。
控制器分离
为了更好的可维护性,我把每条路由指向一个具体的控制器函数:
// controllers/user.controller.js
const User = require('../models/User');
exports.register = async (req, res) => {
const { username, email, password } = req.body;
try {
let user = await User.findOne({ email });
if (user) {
return res.status(400).json({ error: '邮箱已被注册' });
}
user = new User({ username, email, password });
await user.save();
res.status(201).json({ message: '注册成功', user });
} catch (err) {
res.status(500).json({ error: '服务器错误' });
}
};
然后更新路由文件:
// routes/user.routes.js
const express = require('express');
const { register } = require('../controllers/user.controller');
const router = express.Router();
router.post('/register', register);
module.exports = router;
这样我们就完成了一个完整的用户注册流程。后续新增问卷、回答等模块也都采用这种模式。
加入身份验证机制:JWT
为了让用户登录后能够有权限访问受保护的资源(如自己的问卷列表),我们采用了JWT(JSON Web Token)来做认证。
安装依赖:
npm install jsonwebtoken bcryptjs
jsonwebtoken用于生成和校验tokenbcryptjs用来加密用户密码
登录流程示例:
// controllers/auth.controller.js
const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');
const User = require('../models/User');
exports.login = async (req, res) => {
const { email, password } = req.body;
try {
const user = await User.findOne({ email });
if (!user) {
return res.status(400).json({ error: '用户不存在' });
}
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) {
return res.status(400).json({ error: '密码不正确' });
}
const token = jwt.sign({ id: user._id }, 'secretkey', { expiresIn: '1h' });
res.json({ token });
} catch (err) {
res.status(500).json({ error: '服务器错误' });
}
};
前端拿到token之后,保存到localStorage,并在每次请求中带上Authorization: Bearer <token>头。
然后我们可以自定义一个中间件校验token:
// middleware/auth.middleware.js
const jwt = require('jsonwebtoken');
exports.auth = (req, res, next) => {
const token = req.header('Authorization')?.replace('Bearer ', '');
if (!token) {
return res.status(401).json({ error: '未提供token' });
}
try {
const decoded = jwt.verify(token, 'secretkey');
req.user = decoded; // 把用户ID存到req中供后续使用
next();
} catch (e) {
res.status(401).json({ error: '无效的token' });
}
};
再将该中间件应用到受保护的接口上:
router.get('/me', auth, (req, res) => {
res.json({ msg: `欢迎回来,ID为${req.user.id}的用户` });
});
这样基本的身份验证机制就完成了。
接口调试与跨域问题
前端Vue开发时使用的地址是http://localhost:8080,而后端Node服务运行在http://localhost:3000,这时候就会出现跨域问题(CORS)。
为了解决这个问题,我安装了一个中间件:
npm install cors
使用方法很简单:
// app.js
const cors = require('cors');
app.use(cors({
origin: 'http://localhost:8080', // 允许前端域名访问
credentials: true // 允许携带cookies
}));
有时候还需要配合前端设置代理来简化开发阶段的请求发送。
性能优化与部署准备
到了部署前,我还做了几项性能提升的工作:
- 使用PM2守护Node进程:避免服务崩溃自动重启,还能开启集群模式利用多核CPU。
- GZip压缩HTTP响应体:减少传输体积,提升用户体验。
- 接入Redis缓存热点数据:例如问卷首页的热门调查结果。
- 日志记录与监控:使用Winston记录请求日志和错误信息,方便排查问题。
生产环境一般使用Nginx做反向代理,把请求转发给Node服务,同时处理静态资源和服务负载均衡。
实际效果与收获
经过两个月的努力,我们的问卷系统正式上线,目前已经有超过500家企业的用户注册使用。Node.js的表现非常稳定,响应速度快、并发能力强,尤其是在处理异步IO方面确实比传统PHP有明显优势。
个人成长方面,我现在不仅掌握了Node.js的基础能力,也开始理解Node背后的设计哲学——即非阻塞、事件驱动、以及模块化编程的优势。
给新手的几点建议
从基础做起,不要一开始就追求完美架构。先把Hello World跑起来,再逐步加入中间件、数据库支持。
善用Express官方文档,遇到问题第一反应不是Stack Overflow,而是查看权威资料。
学会用Postman测试接口,比写前端更快更直观。
重视安全防护,比如密码加密、XSS过滤、防止暴力破解等等。
关注Node.js版本更新,及时跟进新特性,比如ES Modules(不需要require)、Top-level await等。
写注释,写文档,规范命名。后期维护时真的会感谢现在的自己。
写在最后:Node.js不只是服务端工具
通过这段时间的实践,我发现Node.js不仅仅是后端利器,它还能很好地融合进前端工程流中,比如:
- 做本地Mock Server模拟接口
- 编写自动化脚本处理重复性工作
- 制作小型CLI工具提高开发效率
如果你现在只是个前端开发者,不妨试着往Node方向迈一小步。你会发现自己解锁了更多可能性,也能更好地理解前后端协作的本质。
希望这篇文章对你有所启发,也欢迎留言交流,我们一起进步 🙌

评论 0