Vue3 项目实战 — Vue3记账本项目(前端Vue3,后端node.js)
'# Vue3 项目实战 — Vue3记账本项目(前端Vue3,后端node.js)
一、背景与问题
在个人财务管理场景中,记账本系统是基础而重要的工具。传统Web应用通常采用前后端分离架构,Vue3作为新一代前端框架,结合Node.js作为后端服务,可以构建高性能的记账本系统。本项目将深度解析Vue3与Node.js的集成方案,重点探讨响应式数据驱动、前后端通信机制、数据持久化策略等关键技术。
二、基本原理
1. 响应式系统原理
Vue3采用Proxy实现响应式系统,通过ref和reactive创建响应式数据。当数据变化时,视图会自动更新,这基于JavaScript的Proxy特性实现。
// 响应式数据创建示例
const account = ref({
id: 1,
name: '工资收入',
amount: 10000,
date: new Date()
});2. 前后端通信机制
采用RESTful API进行前后端通信,通过Axios实现HTTP请求。Node.js后端使用Express框架处理请求,MongoDB作为数据存储。
3. 数据持久化策略
使用MongoDB的ObjectId作为主键,通过Mongoose进行数据建模。在记账本场景中,需要考虑多维度查询(时间范围、分类、用户等)。
三、环境准备
1. 前端开发环境
- Node.js >= 18
- Vue3 CLI
- VSCode + Volar插件
- Postman(API调试)
2. 后端开发环境
- Node.js >= 18
- Express >= 4.18
- MongoDB >= 5.0
- Mongoose >= 6.12
3. 项目结构
vue3-budget-app/
├── frontend/ # 前端项目
│ ├── public/
│ ├── src/
│ │ ├── assets/
│ │ ├── components/
│ │ ├── views/
│ │ └── main.js
│ └── index.html
├── backend/ # 后端项目
│ ├── models/
│ ├── routes/
│ ├── controllers/
│ └── server.js
└── .env四、核心实现
1. 前端响应式数据管理
使用Composition API实现数据绑定,通过ref和reactive创建响应式数据。
<template>
<div>
<input v-model="newEntry.name" placeholder="收支名称">
<input type="number" v-model.number="newEntry.amount" placeholder="金额">
<button @click="addEntry">添加</button>
</div>
</template>
<script>
import { ref } from 'vue';
export default {
setup() {
const newEntry = ref({
name: '',
amount: 0,
date: new Date()
});
const addEntry = async () => {
try {
const response = await axios.post('/api/entries', newEntry.value);
console.log('添加成功:', response.data);
} catch (error) {
console.error('添加失败:', error);
}
};
return { newEntry, addEntry };
}
};
</script>关键点解释:
v-model双向绑定实现表单数据同步v-model.number确保输入为数字类型- 使用Axios封装HTTP请求,封装错误处理
2. 后端RESTful API实现
使用Express创建API接口,通过Mongoose连接MongoDB。
// backend/routes/entries.js
const express = require('express');
const router = express.Router();
const Entry = require('../models/Entry');
router.post('/', async (req, res) => {
try {
const entry = new Entry(req.body);
await entry.save();
res.status(201).json(entry);
} catch (error) {
res.status(400).json({ error: error.message });
}
});
module.exports = router;关键点解释:
- 使用Mongoose的
save()方法持久化数据 - 异常处理返回标准错误格式
- 采用RESTful设计规范(POST创建资源)
3. 数据持久化实现
使用Mongoose定义数据模型,添加索引优化查询性能。
// backend/models/Entry.js
const mongoose = require('mongoose');
const EntrySchema = new mongoose.Schema({
name: { type: String, required: true },
amount: { type: Number, required: true },
date: { type: Date, default: Date.now },
category: { type: String, required: true }
}, {
timestamps: true
});
// 添加索引优化查询
EntrySchema.index({ name: 1, category: 1 });
module.exports = mongoose.model('Entry', EntrySchema);关键点解释:
- 使用索引提升多维度查询性能
- 设置
timestamps自动记录创建/更新时间 - 索引字段选择对查询最频繁的字段
五、完整案例
1. 记账本完整系统架构
前端:Vue3 + TypeScript + Axios
后端:Node.js + Express + Mongoose
数据库:MongoDB2. 前端完整代码示例
<template>
<div class="app">
<header class="header">
<h1>记账本</h1>
<div class="date">
<input type="date" v-model="currentDate">
</div>
</header>
<main class="main">
<div class="form">
<input v-model="newEntry.name" placeholder="收支名称">
<input type="number" v-model.number="newEntry.amount" placeholder="金额">
<select v-model="newEntry.category">
<option value="income">收入</option>
<option value="expense">支出</option>
</select>
<button @click="addEntry">添加</button>
</div>
<div class="entries">
<div v-for="entry in entries" :key="entry._id" class="entry">
<div>{{ entry.name }}</div>
<div class="amount">{{ entry.amount }}</div>
<div class="category">{{ entry.category }}</div>
</div>
</div>
</main>
</div>
</template>
<script>
import { ref, onMounted } from 'vue';
import axios from 'axios';
export default {
setup() {
const entries = ref([]);
const currentDate = ref(new Date().toISOString().split('T')[0]);
const newEntry = ref({
name: '',
amount: 0,
category: 'income',
date: currentDate.value
});
const fetchEntries = async () => {
try {
const response = await axios.get('/api/entries', {
params: { date: currentDate.value }
});
entries.value = response.data;
} catch (error) {
console.error('获取数据失败:', error);
}
};
const addEntry = async () => {
try {
const response = await axios.post('/api/entries', newEntry.value);
console.log('添加成功:', response.data);
// 重置表单
newEntry.value.name = '';
newEntry.value.amount = 0;
newEntry.value.category = 'income';
} catch (error) {
console.error('添加失败:', error);
}
};
onMounted(() => {
fetchEntries();
});
return { entries, currentDate, newEntry, addEntry };
}
};
</script>
<style scoped>
/* 样式略 */
</style>3. 后端完整代码示例
// backend/server.js
const express = require('express');
const mongoose = require('mongoose');
const cors = require('cors');
const routes = require('./routes/entries');
const app = express();
const PORT = process.env.PORT || 3000;
// 中间件
app.use(cors());
app.use(express.json());
// 路由
app.use('/api/entries', routes);
// 数据库连接
mongoose.connect('mongodb://localhost:27017/budget', {
useNewUrlParser: true,
useUnifiedTopology: true
}).then(() => {
console.log('MongoDB连接成功');
app.listen(PORT, () => {
console.log(`服务器运行在 http://localhost:${PORT}`);
});
}).catch((err) => {
console.error('MongoDB连接失败:', err);
});六、源码解析
1. 前端响应式系统
Vue3的响应式系统基于Proxy实现,通过ref创建的响应式数据会自动追踪依赖。当数据变化时,视图会触发更新。
2. 后端路由处理
Express的路由处理遵循RESTful规范,POST请求用于创建新资源,GET请求用于查询资源。通过中间件处理跨域请求。
3. 数据库连接
使用Mongoose连接MongoDB,配置连接参数,建立数据库连接后启动服务。添加索引优化查询性能。
七、进阶使用
1. 分页与过滤
在前端实现分页功能,后端支持按日期范围、分类等条件过滤数据。
// 前端分页代码示例
const page = ref(1);
const pageSize = ref(10);
const fetchEntries = async () => {
try {
const response = await axios.get('/api/entries', {
params: {
date: currentDate.value,
page: page.value,
limit: pageSize.value
}
});
entries.value = response.data.entries;
totalPages.value = response.data.totalPages;
} catch (error) {
console.error('获取数据失败:', error);
}
};2. 数据可视化
使用ECharts或Chart.js实现数据可视化,展示收支趋势。
<template>
<div ref="chart" style="width: 100%; height: 400px;"></div>
</template>
<script>
import * as echarts from 'echarts';
export default {
mounted() {
const chart = echarts.init(this.$refs.chart);
chart.setOption({
xAxis: {
type: 'category',
data: this.entries.map(e => e.date)
},
yAxis: {
type: 'value'
},
series: [{
type: 'line',
data: this.entries.map(e => e.amount)
}]
});
}
};
</script>八、性能与工程实践
1. 前端性能优化
- 使用懒加载组件(Vue3的
<Suspense>) - 使用Vue3的
keep-alive缓存动态组件 - 启用生产环境的代码分割(Vue CLI配置)
2. 后端性能优化
- 使用数据库索引优化查询
- 使用缓存(Redis)存储热点数据
- 使用连接池管理数据库连接
3. 安全实践
- 使用JWT进行身份认证
- 验证用户输入防止注入攻击
- 设置CORS策略限制跨域请求
4. 异常处理
- 前端使用try/catch处理异步请求
- 后端使用
express-async-errors处理异步错误 - 记录错误日志到文件或日志系统
九、常见问题与踩坑
1. 跨域问题
现象:浏览器报错No 'Access-Control-Allow-Origin' header
解决:后端使用cors中间件,前端配置代理
// 前端vue.config.js配置代理
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'http://localhost:3000',
changeOrigin: true,
pathRewrite: { '^/api': '' }
}
}
}
};2. 数据类型不一致
现象:金额字段在前端显示为字符串
解决:使用v-model.number确保类型一致
3. 数据库连接失败
现象:启动后无法连接MongoDB
解决:检查数据库是否运行,检查连接字符串是否正确
十、最佳实践
1. 前端最佳实践
- 使用TypeScript增强类型检查
- 使用Vuex/Pinia管理全局状态
- 使用Vite替代Webpack提升构建速度
2. 后端最佳实践
- 使用Express-Router组织路由
- 使用Mocha/ Jest进行单元测试
- 使用PM2管理Node.js进程
3. 安全最佳实践
- 使用Helmet设置安全头
- 使用helmet-csrf防止CSRF攻击
- 使用bcrypt加密用户密码
十一、总结
本项目通过Vue3和Node.js构建了一个完整的记账本系统,深入探讨了响应式系统、前后端通信、数据持久化等关键技术。在实际开发中,这种方案适用于中小型项目,特别是在需要快速开发和迭代的场景中。需要注意的是,对于大规模系统,需要考虑更复杂的架构设计,如微服务、分布式系统等。通过本项目,开发者可以掌握前后端分离开发的核心技术,为构建更复杂的系统打下坚实基础。
评论已关闭