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

长安码客
2025-06-16 16:47
阅读 548

引言:我的Node.js初体验

引言:我的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用于生成和校验token
  • bcryptjs用来加密用户密码

登录流程示例:

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

有时候还需要配合前端设置代理来简化开发阶段的请求发送。


性能优化与部署准备

到了部署前,我还做了几项性能提升的工作:

  1. 使用PM2守护Node进程:避免服务崩溃自动重启,还能开启集群模式利用多核CPU。
  2. GZip压缩HTTP响应体:减少传输体积,提升用户体验。
  3. 接入Redis缓存热点数据:例如问卷首页的热门调查结果。
  4. 日志记录与监控:使用Winston记录请求日志和错误信息,方便排查问题。

生产环境一般使用Nginx做反向代理,把请求转发给Node服务,同时处理静态资源和服务负载均衡。


实际效果与收获

经过两个月的努力,我们的问卷系统正式上线,目前已经有超过500家企业的用户注册使用。Node.js的表现非常稳定,响应速度快、并发能力强,尤其是在处理异步IO方面确实比传统PHP有明显优势。

个人成长方面,我现在不仅掌握了Node.js的基础能力,也开始理解Node背后的设计哲学——即非阻塞、事件驱动、以及模块化编程的优势。


给新手的几点建议

  1. 从基础做起,不要一开始就追求完美架构。先把Hello World跑起来,再逐步加入中间件、数据库支持。

  2. 善用Express官方文档,遇到问题第一反应不是Stack Overflow,而是查看权威资料。

  3. 学会用Postman测试接口,比写前端更快更直观。

  4. 重视安全防护,比如密码加密、XSS过滤、防止暴力破解等等。

  5. 关注Node.js版本更新,及时跟进新特性,比如ES Modules(不需要require)、Top-level await等。

  6. 写注释,写文档,规范命名。后期维护时真的会感谢现在的自己。


写在最后:Node.js不只是服务端工具

通过这段时间的实践,我发现Node.js不仅仅是后端利器,它还能很好地融合进前端工程流中,比如:

  • 做本地Mock Server模拟接口
  • 编写自动化脚本处理重复性工作
  • 制作小型CLI工具提高开发效率

如果你现在只是个前端开发者,不妨试着往Node方向迈一小步。你会发现自己解锁了更多可能性,也能更好地理解前后端协作的本质。

希望这篇文章对你有所启发,也欢迎留言交流,我们一起进步 🙌

评论 0

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