1. 项目概述与环境准备
1.1 这套技术栈到底在做什么
先别急着敲代码,咱们花两分钟搞清楚这套组合拳的定位,后面会少走很多弯路。
Vue3 + Node.js + MongoDB 是目前个人博主、独立开发者和小型创业团队最常用的全栈组合之一。简单来说,Vue3负责浏览器端的界面渲染,Node.js负责服务端接口和业务逻辑,MongoDB负责数据存储。三个角色各司其职,而阿里的云服务器则负责让这台"三驾马车"能够7x24小时对外提供访问服务。
我见过太多人一上来就照着某个"一键部署脚本"咔咔往下走,结果前端构建产物不知道往哪放、Node进程在SSH断开之后就挂了、MongoDB没做鉴权和公网裸奔,最后部署出来的项目既不稳定又不安全。这个教程就是要把这些坑一个个提前填平。
适合谁来参考?三种人:一是刚学完前端三件套和Vue想尝试全栈的小白;二是本地开发跑得通但从未上过云服务器的程序员;三是需要快速交付Demo或内部工具、不想折腾K8s和Docker满汉全席的团队。
1.2 本机环境:Windows与macOS通用策略
部署的前半段是本地开发和联调,后半段才上阿里云。所以先把本机环境配好。
我需要先强调一个原则:版本锁定。Node.js版本太新或太旧都会带来一堆莫须有的问题,比如error installing 24.20.0: node.js v24.20.0 is not yet released or is not available这种报错,十有八九是版本管理器和下载源之间的同步没有跟上。因此,咱们不用最新版,而是选一个当前生态兼容性最好的长期支持版本,比如Node.js 20 LTS。
bash复制# Windows:直接去Node.js官网下载v20.x LTS的msi包
# macOS:建议用nvm管理,避免日后切换版本时权限问题
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
nvm install 20
nvm use 20
node -v
npm -v
安装完成后顺手把npm的镜像源切到国内,这一步能省下后面npm install的大量等待时间:
bash复制npm config set registry https://registry.npmmirror.com
MongoDB在Windows上安装比较简单,从官方或者国内镜像站下载msi安装包,安装时注意取消勾选"Install MongoDB Compass",Compass虽然是官方图形化工具,但后面我们用DBeaver统一管理就行,没必要再占一份内存。macOS用户用Homebrew更舒服:
bash复制brew tap mongodb/brew
brew install mongodb-community@7.0
brew services start mongodb-community
到这里,本机环境的基础已经搭好。有一个细节值得提:MongoDB安装失败的场景很常见,尤其在Windows上,多半是360、火绒这类安全软件拦截了Windows Service的注册。如果你也遇到mongodb安装失败,先退出安全软件,再以管理员身份运行安装程序,成功率会高出一大截。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 后端接口设计与MongoDB数据建模
2.1 用Express还是NestJS?从项目规模说起
Node.js生态里的服务端框架很多,但在Vue3 + MongoDB的组合中,最主流的选择是Express和NestJS。Express轻量、灵活、学习曲线平缓;NestJS则更像Java Spring的风格,有依赖注入、模块化、装饰器,适合业务复杂的大项目。
如果是个人项目、毕设、中小型管理后台,我强烈建议用Express。不是NestJS不好,而是它的TS装饰器、模块组织方式对新手来说理解成本太高,而且一个简单CRUD接口在NestJS里要建Controller、Service、Module三个文件,写着写着就容易陷入"结构焦虑"。反过来,Express一百行就能跑通一个完整的用户注册、登录、增删改查接口链路,先把业务跑起来比什么都重要。
举一个实际的项目目录结构,这是我在多个可运维项目中实战验证过的布局:
text复制server/
├── config/
│ └── db.js # 数据库连接配置
├── models/
│ └── userModel.js # 用户数据模型
├── routes/
│ └── userRoutes.js # 用户相关路由
├── middleware/
│ └── auth.js # JWT鉴权中间件
├── .env # 环境变量(不提交到Git)
├── .env.example # 环境变量示例(提交到Git)
├── package.json
└── server.js # 入口文件
为什么要强制把配置、模型、路由拆分成独立目录?因为项目上线之后,排查问题时如果能直接定位到"模型层的问题"或者"路由层的问题",效率完全不一样。一个把所有代码塞在一个文件里的项目,维护成本会随着业务体量指数级上升。
2.2 Mongoose建模:给文档数据立规矩
MongoDB本身是NoSQL,意味着它不强制表结构。但是如果在业务代码里不设立约束,三个月后你打开数据库,会发现同一条业务的数据字段五花八门,有的字段叫createTime,有的叫created_at,甚至有的根本没有这个字段。所以哪怕MongoDB不强制,我们也要用Mongoose这类ODM(对象文档映射工具)来给数据"立规矩"。
下面以最典型的用户模型为例:
javascript复制// models/userModel.js
const mongoose = require('mongoose');
const userSchema = new mongoose.Schema({
username: {
type: String,
required: [true, '用户名不能为空'],
unique: true,
trim: true,
minlength: 3
},
email: {
type: String,
required: [true, '邮箱不能为空'],
unique: true,
lowercase: true,
match: [/^\S+@\S+\.\S+$/, '邮箱格式不正确']
},
passwordHash: {
type: String,
required: true,
select: false // 查询时默认不返回该字段,防止泄露
},
avatar: {
type: String,
default: ''
},
role: {
type: String,
enum: ['user', 'admin'],
default: 'user'
},
status: {
type: Number,
enum: [0, 1], // 0-禁用 1-正常
default: 1
}
}, {
timestamps: true // 自动维护 createdAt 和 updatedAt
});
module.exports = mongoose.model('User', userSchema);
有几个细节值得注意。select: false配合passwordHash字段使用,可以在查询时默认不返回密码哈希,从源头避免密码被意外输出到日志或接口响应里。timestamps: true自动维护的两个时间字段,在很多业务场景下都够用,没必要再手写一个createTime。
密码不能明文保存,这是底线中的底线。用bcryptjs做哈希,成本因子设为10,对个人项目来说安全性和性能平衡得比较好:
javascript复制const bcrypt = require('bcryptjs');
const saltRounds = 10;
async function hashPassword(plainPassword) {
return await bcrypt.hash(plainPassword, saltRounds);
}
async function verifyPassword(plainPassword, passwordHash) {
return await bcrypt.compare(plainPassword, passwordHash);
}
2.3 接口鉴权:JWT还是Session?
传统服务端开发习惯用Session,前端登录后拿到一个sessionId存在Cookie里。但前后端分离架构下,Vue3跑的静态页面和你Node.js接口可能不在同一个域名下,Cookie跨域处理会变得特别麻烦。所以在移动端和Web端共存的项目里,JWT(JSON
