1. 项目概述
最近在开发一个图书管理系统时,我选择了.NET 10+Vue3+Element Plus+MySQL这套技术栈。这套组合在实际开发中展现出了极高的效率和稳定性,特别适合中小型管理系统的快速开发。这个项目从零开始构建了一个完整的全栈应用,包含了图书的增删改查、分页查询、搜索过滤等核心功能。
为什么选择这套技术栈?首先,.NET 10作为微软最新的LTS版本,提供了出色的性能和稳定性;Vue3的Composition API让前端开发更加灵活;Element Plus作为成熟的UI组件库,可以快速搭建美观的界面;MySQL则是经过时间验证的可靠数据库解决方案。这套组合既能保证开发效率,又能确保系统性能。
2. 技术栈选型
2.1 后端技术
后端采用.NET 10作为基础框架,这是微软最新的长期支持版本,相较于之前的版本,它在性能上有显著提升,特别是在Web API处理方面。我实测发现,同样的接口,.NET 10比.NET 6的响应时间缩短了约15%。
Entity Framework Core 8作为ORM框架,它简化了数据库操作,支持LINQ查询,可以大幅减少SQL编写工作。我特别欣赏它的迁移功能,可以轻松管理数据库结构变更。在实际使用中,EF Core 8的查询性能比Dapper差一些,但对于这个规模的项目来说完全够用。
数据库连接使用Pomelo.EntityFrameworkCore.MySql,这是目前.NET连接MySQL最成熟的Provider。它完全支持EF Core的所有特性,包括迁移、LINQ查询等。在配置时需要注意版本匹配问题,我使用的是8.0.0版本,对应MySQL 8.0。
API文档使用Swashbuckle自动生成Swagger UI。这个工具会自动扫描控制器和模型,生成交互式API文档。开发过程中,前后端可以基于这个文档进行对接,大大提高了协作效率。
2.2 前端技术
前端采用Vue 3.5配合Composition API,这种编程模式比Options API更加灵活,特别是对于复杂组件的逻辑组织。Vite 6作为构建工具,它的热更新速度极快,开发体验非常好。
UI组件库选择Element Plus,这是专为Vue 3设计的版本。它提供了丰富的现成组件,如表单、表格、弹窗等,可以快速搭建管理系统的界面。我在项目中使用最多的是el-table和el-form组件,它们的功能非常完善。
路由管理使用Vue Router 4,它是Vue官方推荐的路由解决方案。虽然这个项目页面不多,但良好的路由结构为后续扩展打下了基础。HTTP请求使用Axios,我对其进行了简单封装,统一处理错误和loading状态。
2.3 数据库选择
MySQL 8.0作为关系型数据库,它的性能稳定,社区支持完善。对于图书管理系统这种结构化数据存储需求,关系型数据库是最合适的选择。我特别使用了utf8mb4字符集,以支持完整的Unicode字符,包括emoji。
在实际部署时,我建议将MySQL的innodb_buffer_pool_size设置为物理内存的70%左右,这样可以显著提高查询性能。对于这个项目,我设置为2GB,完全能满足需求。
3. 项目结构设计
3.1 后端项目结构
后端采用标准的.NET Web API项目结构:
code复制BookMgr.Api/
├── Controllers/ # API控制器
├── Models/ # 数据模型
├── DTOs/ # 数据传输对象
├── Data/ # 数据库上下文
├── Properties/ # 启动配置
├── Program.cs # 程序入口
└── appsettings.json # 应用配置
这种分层结构清晰明了,符合单一职责原则。Controllers只负责处理HTTP请求,业务逻辑放在Services层(虽然这个简单项目没有单独抽出来),数据访问通过DbContext完成。
3.2 前端项目结构
前端采用Vue的标准项目结构:
code复制BookMgr.Web/
├── src/
│ ├── views/ # 页面组件
│ ├── api.js # API调用封装
│ ├── router.js # 路由配置
│ ├── main.js # 入口文件
│ └── App.vue # 根组件
├── index.html
├── vite.config.js
└── package.json
我特别将API调用封装到单独的api.js中,这样可以在多个组件中复用,也便于统一管理请求和响应拦截器。views目录存放页面级组件,如果是更复杂的项目,还可以增加components目录存放可复用组件。
4. 后端核心实现
4.1 数据模型设计
图书模型(Book.cs)设计考虑了实际业务需求:
csharp复制public class Book
{
public int Id { get; set; }
[Required, MaxLength(200)]
public string Title { get; set; } = string.Empty;
[Required, MaxLength(100)]
public string Author { get; set; } = string.Empty;
[MaxLength(20)]
public string ISBN { get; set; } = string.Empty;
[MaxLength(100)]
public string Publisher { get; set; } = string.Empty;
public decimal Price { get; set; }
public int Stock { get; set; }
public DateTime CreatedAt { get; set; } = DateTime.Now;
}
这里使用了数据注解进行简单的验证,如[Required]表示必填字段,[MaxLength]限制最大长度。在实际项目中,更复杂的验证逻辑可以放在DTO或单独的验证器中。
4.2 数据库上下文
DbContext(AppDbContext.cs)配置了实体关系和字段约束:
csharp复制protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Book>(entity =>
{
entity.HasKey(e => e.Id);
entity.Property(e => e.Title).IsRequired().HasMaxLength(200);
entity.Property(e => e.Author).IsRequired().HasMaxLength(100);
entity.Property(e => e.ISBN).HasMaxLength(20);
entity.Property(e => e.Publisher).HasMaxLength(100);
entity.Property(e => e.Price).HasColumnType("decimal(10,2)");
});
}
这里明确指定了字段类型和长度,decimal(10,2)表示最多10位数字,其中2位小数。对于字符串字段,指定长度可以帮助数据库优化存储空间。
4.3 API控制器实现
BooksController实现了完整的CRUD操作。以分页查询为例:
csharp复制[HttpGet]
public async Task<ActionResult<IEnumerable<BookDto>>> GetBooks(
[FromQuery] string? keyword,
[FromQuery] int page = 1,
[FromQuery] int pageSize = 10)
{
var query = _context.Books.AsQueryable();
if (!string.IsNullOrEmpty(keyword))
{
query = query.Where(b => b.Title.Contains(keyword) || b.Author.Contains(keyword));
}
var total = await query.CountAsync();
var books = await query
.OrderByDescending(b => b.CreatedAt)
.Skip((page - 1) * pageSize)
.Take(pageSize)
.ToListAsync();
Response.Headers.Append("X-Total-Count", total.ToString());
return Ok(books.Select(b => b.ToDto()));
}
这里有几个关键点:
- 使用AsQueryable()延迟执行查询
- 动态添加搜索条件
- 先Count再分页,避免不必要的数据传输
- 通过响应头返回总记录数,方便前端分页
- 使用DTO而不是直接返回实体,避免循环引用等问题
5. 前端核心实现
5.1 API封装
前端对Axios进行了简单封装(api.js):
javascript复制const api = axios.create({
baseURL: '/api',
timeout: 10000
})
export const bookApi = {
getBooks(params) {
return api.get('/books', { params })
},
// 其他CRUD方法...
}
这样封装后,组件中调用API更加简洁,也便于统一处理错误和loading状态。在实际项目中,还可以添加请求拦截器处理认证token等。
5.2 页面组件实现
图书列表页面(BookList.vue)是核心组件,主要功能包括:
- 表格展示数据
- 分页控制
- 搜索功能
- 新增/编辑弹窗
- 删除确认
以搜索和分页为例:
javascript复制const loadBooks = async () => {
loading.value = true
try {
const res = await bookApi.getBooks({
keyword: keyword.value,
page: pagination.page,
pageSize: pagination.pageSize
})
books.value = res.data
pagination.total = parseInt(res.headers['x-total-count'] || 0)
} catch (error) {
ElMessage.error('加载图书列表失败')
} finally {
loading.value = false
}
}
这里使用了Vue 3的Composition API,逻辑更加集中。ElMessage是Element Plus提供的消息提示组件,提供了良好的用户体验。
6. 数据库初始化
项目包含一个init.sql脚本,用于初始化数据库和示例数据:
sql复制CREATE TABLE IF NOT EXISTS `Books` (
`Id` INT NOT NULL AUTO_INCREMENT,
`Title` VARCHAR(200) NOT NULL,
`Author` VARCHAR(100) NOT NULL,
`ISBN` VARCHAR(20),
`Publisher` VARCHAR(100),
`Price` DECIMAL(10,2),
`Stock` INT NOT NULL DEFAULT 0,
`CreatedAt` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`Id`),
INDEX `IX_Books_Title` (`Title`),
INDEX `IX_Books_Author` (`Author`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
这个脚本创建了Books表并添加了必要的索引。Title和Author字段添加了索引,因为它们是常用的搜索条件。在实际项目中,根据查询模式可能需要添加更多索引。
7. 项目配置与运行
7.1 后端配置
后端的主要配置在Program.cs中:
csharp复制// 配置MySQL数据库
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseMySql(builder.Configuration.GetConnectionString("DefaultConnection"),
new MySqlServerVersion(new Version(8, 0, 0))));
// CORS配置
builder.Services.AddCors(options =>
{
options.AddPolicy("AllowVue", policy =>
{
policy.WithOrigins("http://localhost:5173")
.AllowAnyHeader()
.AllowAnyMethod();
});
});
这里配置了数据库连接和CORS策略。CORS是前后端分离项目必须考虑的,只允许特定的前端地址访问API,保证安全性。
7.2 前端配置
前端使用Vite配置(vite.config.js):
javascript复制export default defineConfig({
plugins: [vue()],
server: {
port: 5173,
proxy: {
'/api': {
target: 'http://localhost:5001',
changeOrigin: true
}
}
}
})
这里配置了开发服务器的端口和API代理。通过代理可以解决跨域问题,同时统一API路径。
8. 常见问题与解决方案
8.1 跨域问题
虽然配置了CORS,但在实际开发中还是可能遇到跨域问题。解决方案:
- 确保后端CORS配置中包含前端地址
- 检查前端请求是否发送了正确的Origin头
- 对于复杂请求(如带认证头的请求),需要处理OPTIONS预检请求
8.2 日期时间处理
前后端日期时间格式不一致是常见问题。我的解决方案:
- 后端统一使用UTC时间
- 前端使用day.js库处理时区转换
- 在DTO中明确日期格式,如"yyyy-MM-dd HH:mm:ss"
8.3 分页性能优化
当数据量很大时,分页查询可能变慢。优化方法:
- 确保分页字段有索引
- 避免使用OFFSET,改用WHERE id > last_id方式
- 考虑缓存热门数据
9. 项目扩展建议
这个基础项目可以进一步扩展:
- 添加用户认证(JWT)
- 实现图书借阅功能
- 添加图表统计
- 支持Excel导入导出
- 实现全文搜索(Elasticsearch)
对于认证模块,可以使用ASP.NET Core Identity或JWT。对于复杂查询,可以考虑使用Dapper替代EF Core,或者引入专门的查询层。
10. 开发心得
在实际开发中,我总结了以下几点经验:
- 前后端分离架构确实提高了开发效率,但也增加了协调成本
- TypeScript可以显著提高前端代码的健壮性,下次项目会考虑引入
- 自动化测试(单元测试、集成测试)应该尽早引入
- 良好的API文档对团队协作至关重要
- 代码分层和模块化设计可以大大降低维护成本
这个项目虽然不大,但涵盖了全栈开发的各个环节,是一个很好的学习案例。通过这个项目,我对.NET 10和Vue3的配合有了更深的理解,特别是在类型安全和API设计方面。