Web网站开发毕设新手指南:从零搭建可部署的全栈项目
摘要:许多计算机专业学生在完成Web网站开发毕设时,常因缺乏工程经验陷入技术选型混乱、前后端耦合严重、部署流程复杂等困境。本文面向新手,提供一套轻量、可落地的全栈开发路径,基于现代主流技术栈(Vue + Express + SQLite),详解项目初始化、模块解耦、接口设计及一键部署方案。读者可快速掌握毕设项目的核心架构逻辑,避免常见陷阱,高效交付具备完整功能与基础安全性的可运行系统。
1. 背景痛点:为什么毕设总被导师打回?
做毕设最怕“一看就会,一写就废”。我帮导师评审了三年本科毕设,总结出新人们最容易踩的坑:
过度追求“高大上”框架
把简历里的“微服务、分布式”全搬进来,结果本地跑不起来,答辩现场直接 502。前后端揉成一团
HTML 里嵌 JavaScript,JavaScript 里拼 SQL,改一行代码要重启三次,导师一看目录结构就皱眉。本地与线上割裂
实验室里localhost:3000跑得好好的,放到云服务器就 404,静态资源全走绝对路径,跨域报错一屏装不下。忽略安全与性能
注册接口不做校验,一插'; DROP TABLE users;--直接全剧终;上线后 2 核 4 G 被爬虫打爆,内存飙红。
如果你也中过招,别急,下面这套“轻量全栈路线”专为毕设场景打磨:学习成本低、代码量可控、部署一次成功。
2. 技术选型:为什么用 Vue + Express + SQLite?
毕设不是“炫技大会”,而是“能跑就行 + 导师好懂”。下面给出 3 组对比,帮你 5 分钟拍板。
| 维度 | Vue | React | 结论 |
|---|---|---|---|
| 学习曲线 | 模板语法接近原生 HTML | JSX 需额外理解 | 新手友好选 Vue |
| 生态脚手架 | Vite 一键脚手架 | 需额外配 Webpack | Vue 更快 |
| 导师认知度 | 官网中文文档齐全 | 英文资料多 | 答辩更省心 |
| 维度 | Express | Koa | 结论 |
|---|---|---|---|
| 中间件生态 | 丰富、文档多 | 精简、需自拼 | 毕设求稳选 Express |
| 教程数量 | 中文博客成堆 | 相对少 | 踩坑 Google 能搜到 |
| 维度 | SQLite | MySQL | 结论 |
|---|---|---|---|
| 安装成本 | 0,文件即库 | 需装服务 | 个人服务器省事 |
| 备份迁移 | 单文件拷走 | 需导 SQL | 答辩前 U 盘即走 |
| 并发场景 | 毕设 < 50 QPS 足够 | 重量大 | 轻量够用 |
一句话总结:Vue3 + Vite + Express4 + SQLite3是“能跑、好写、易部署”的毕设黄金三角。
3. 项目骨架:前后端分离长啥样?
先上图,目录结构一目了然:
3.1 前端(client 目录)
- 使用
npm create vite@latest client --template vue生成 - 代理配置
vite.config.js解决开发期跨域:
export default defineConfig({ server: { proxy: { '/api': 'http://localhost:3000' } } })3.2 后端(server 目录)
express-generator一键生成骨架- 路由分层:
routes/user.js、routes/todo.js - 数据库文件放
db.sqlite,.gitignore里加一行忽略,防止密钥泄露
3.3 统一脚本
根目录package.json写两条命令:
"scripts": { "dev": "/concurrently \"npm run dev -C client\" \"npm run dev -C server\"", "build": "npm run build -C client && npm run start -C server" }毕设答辩时,一句npm run dev全栈跑通,导师印象分 +10。
4. 核心实现:登录 + 待办列表
下面用“待办列表”贯穿用户认证与 CRUD,代码直接复制可跑。
4.1 数据库初始化
server/db.js
const sqlite3 = require('sqlite3').verbose() const db = new sqlite3.Database('db.sqlite') db.serialize(() => { db.run(`CREATE TABLE IF NOT EXISTS users( id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT UNIQUE, password TEXT )`) db.run(`CREATE TABLE IF NOT EXISTS todos( id INTEGER PRIMARY KEY AUTOINCREMENT, uid INTEGER, title TEXT, done INTEGER DEFAULT 0, FOREIGN KEY(uid) REFERENCES users(id) )`) }) module.exports = db4.2 后端接口
server/routes/user.js(节选注册、登录)
const express = require('express') const bcrypt = require('bcryptjs') const jwt = require('jsonwebtoken') const db = require('../db') const router = express.Router() // 注册 router.post('/register', async (req, res) => { const { username, password } = req.body if (!username || !password) return res.status(400).json({ msg: '参数缺失' }) const hash = await bcrypt.hash(password, 10) db.run('INSERT INTO users (username, password) VALUES (?, ?)', [username, hash], function (err) { if (err && err.code === 'SQLITE_CONSTRAINT') return res.status(409).json({ msg: '用户名已存在' }) res.json({ id: this.lastID }) }) }) // 登录 router.post('/login', (req, res) => { const { username, password } = req.body db.get('SELECT * FROM users WHERE username = ?', [username], async (err, row) => { if (!row || !(await bcrypt.compare(password, row.password))) { return res.status(401).json({ msg: '用户名或密码错误' }) } const token = jwt.sign({ uid: row.id }, process.env.JWT_SECRET, { expiresIn: '7d' }) res.json({ token }) }) }) module.exports = router4.3 前端调用
src/api/request.js(axios 实例)
import axios from 'axios' const request = axios.create({ baseURL: '/api', timeout: 5000 }) // 请求拦截自动带 token request.interceptors.request.use(config => { const token = localStorage.getItem('token') if (token) config.headers.Authorization = `Bearer ${token}` return config }) export default requestsrc/views/Login.vue
import request from '@/api/request' async function login() { const res = await request.post('/user/login', form) localStorage.setItem('token', res.data.token) router.push('/todo') }4.4 待办增删改查
server/routes/todo.js(RESTful 风格)
router.post('/', auth, (req, res) => { /* 新增 */ }) router.get('/', auth, (req, res) => { /* 查列表 */ }) router.put('/:id', auth, (req, res) => { /* 改状态 */ }) router.delete('/:id', auth, (req, res) => { /* 删除 */ })前端对应组件用v-for渲染,按钮绑定@click="del(todo.id)",代码简洁,导师一眼看懂。
5. 性能与安全:别让“小项目”变成“小事故”
CORS
生产环境若前后端分离部署,后端装cors中间件并白名单前端域名,禁止*通配。输入校验
用express-validator一行body('username').isAlphanumeric()把危险字符拦在门外。SQL 注入
所有 SQL 都用?占位符,拒绝拼接;SQLite 虽无存储过程,也要防UNION SELECT。密码安全
明文 = 0 分,MD5 = 不及格,bcrypt + salt 成本 10 是底线。环境变量
把JWT_SECRET、PORT、DB_PATH写进.env,用dotenv加载,GitHub 公开库再也不怕泄露。
6. 生产环境部署:从能跑到稳跑
一键脚本
在package.json加"start": " node server/bin/www",服务器拉完代码直接npm start。进程守护
云主机用pm2 start npm -- start;若用 Vercel,把server文件夹设成 API 目录,自动无宕机。HTTPS
域名 + Let's Encrypt 免费证书,Nginx 反向代理 3000 端口,配置 301 强跳,答辩地址栏一把小锁,导师点头。GitHub Actions 持续集成(加分项)
推送 main 分支自动跑单元测试,再 rsync 到云主机,README 放个 pass 小徽章,印象分再 +10。
7. 结项冲刺:把待办列表升级成“团队看板”
做到这里,你已经拥有:
- 注册/登录/鉴权
- 用户维度隔离的待办 CURD
- 可部署、带 HTTPS 的线上地址
下一步思考:
- 增加“项目-任务”两级分组,模拟真实协作场景
- 用 Socket.io 做实时通知,演示多人同时改动
- 把 SQLite 迁到 MySQL,写迁移脚本,展示“平滑升级”
- 前端加拖拽排序,引用
vuedraggable,让演示更炫酷
把这些写进论文“后续展望”,导师一看:这孩子有产品思维,优秀!
8. 写在最后:动手才是终点
看十遍不如敲一遍。现在打开终端:
npm create vite@latest clientnpx express-generator server- 把上面的登录、待办代码粘进去
- 本地
npm run dev跑通 - push 到 GitHub,Vercel 一键部署
当你把可访问的链接贴到答辩 PPT 里,就会明白:毕设其实可以很简单——选对技术栈、拆好模块、提前部署,剩下的就是讲故事。祝你一次通过,提前毕业!