基于后端项目的前端开发实践记录
📋 项目概述
项目名称 : 比特奥定制报表系统
技术栈 : Vue 3 + Element Plus + Vite (前端) + Spring Boot (后端)
开发模式 : 前后端分离
项目结构: 单体仓库包含前后端代码
🏗️ 项目架构分析
目录结构设计
bitao-defined_report_ui/
├── src/ # 前端源码
│ ├── api/ # API接口层
│ ├── components/ # 组件库
│ │ ├── dict/ # 字典管理组件
│ │ ├── report/ # 报表管理组件
│ │ └── layout/ # 布局组件
│ ├── router/ # 路由配置
│ └── utils/ # 工具函数
├── backend/ # 后端源码
│ └── bitao-defined-report/
└── public/ # 静态资源
技术选型理由
- Vue 3: 组合式API,更好的TypeScript支持
- Element Plus: 成熟的UI组件库,快速开发
- Vite: 快速的构建工具,热更新体验好
- Axios: HTTP客户端,支持拦截器和请求/响应处理
🔄 前后端协作流程
1. API设计先行
后端API规范
java
// 统一响应格式
public class ApiResponse<T> {
private String traceId; // 追踪ID
private boolean result; // 操作结果
private int code; // 状态码
private String msg; // 消息
private T data; // 数据
private boolean success; // 成功标识
}
// 分页参数基类
public class PageObject {
private Integer pageNum = 1;
private Integer pageSize = 10;
private String isAsc = "desc";
}
前端API封装
javascript
// api/reportApi.js
import request from '@/utils/request'
/**
* 获取报表列表
* @param {Object} params 查询参数
* @returns {Promise} API响应
*/
export function getReportList(params) {
return request({
url: '/defined-report/report/selectByPage',
method: 'post',
data: params
})
}
2. 数据类型对齐
关键经验:类型一致性
场景 | 后端类型 | 前端处理 | 注意事项 |
---|---|---|---|
布尔状态 | Boolean |
null/true/false |
避免空字符串 |
分页参数 | Integer |
Number |
确保数值类型 |
时间字段 | LocalDateTime |
String |
统一格式化 |
枚举值 | Enum |
String/Number |
保持值一致 |
实际案例:状态字段处理
javascript
// ❌ 错误做法
const queryParams = {
usableStatus: '' // 空字符串无法转换为Boolean
}
// ✅ 正确做法
const queryParams = {
usableStatus: null // null表示不筛选,true/false表示具体状态
}
🎨 组件开发模式
1. 页面组件结构
标准页面模板
vue
<template>
<div class="page-container">
<!-- 查询表单 -->
<div class="search-form">
<el-form :model="queryParams" :inline="true">
<!-- 查询条件 -->
</el-form>
</div>
<!-- 操作工具栏 -->
<div class="toolbar">
<el-button type="primary" @click="handleAdd">新增</el-button>
</div>
<!-- 数据表格 -->
<el-table :data="dataList" v-loading="loading">
<!-- 表格列 -->
</el-table>
<!-- 分页组件 -->
<pagination
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
:total="total"
@pagination="getList"
/>
</div>
</template>
<script setup>
import { reactive, ref, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
// 响应式数据
const loading = ref(false)
const dataList = ref([])
const total = ref(0)
// 查询参数
const queryParams = reactive({
pageNum: 1,
pageSize: 10,
// 其他查询字段
})
// 获取列表数据
const getList = async () => {
loading.value = true
try {
const response = await apiMethod(queryParams)
if (response.code === 200) {
dataList.value = response.data.data || []
total.value = response.data.total || 0
} else {
ElMessage.error(response.message || '获取数据失败')
}
} catch (error) {
console.error('获取数据失败:', error)
ElMessage.error('网络错误,请稍后重试')
} finally {
loading.value = false
}
}
// 页面初始化
onMounted(() => {
getList()
})
</script>
2. 组件复用策略
通用组件抽取
javascript
// components/pub/Pagination.vue - 分页组件
// components/layout/AppHeader.vue - 页面头部
// components/layout/Sidebar.vue - 侧边栏
业务组件分类
javascript
// components/dict/ - 字典管理相关组件
// components/report/ - 报表管理相关组件
// ├── management/ - 报表列表管理
// ├── designer/ - 报表设计器
// └── preview/ - 报表预览
🔧 开发工具配置
1. Vite配置优化
javascript
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
'@': path.resolve(__dirname, 'src')
}
},
server: {
port: 3000,
proxy: {
'/defined-report': {
target: 'http://localhost:8080',
changeOrigin: true
}
}
}
})
2. 请求拦截器配置
javascript
// utils/request.js
import axios from 'axios'
import { ElMessage } from 'element-plus'
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API,
timeout: 5000
})
// 请求拦截器
service.interceptors.request.use(
config => {
// 添加token等通用处理
return config
},
error => {
console.log(error)
return Promise.reject(error)
}
)
// 响应拦截器
service.interceptors.response.use(
response => {
const res = response.data
// 统一错误处理
if (res.code !== 200) {
ElMessage.error(res.message || '系统错误')
return Promise.reject(new Error(res.message || 'Error'))
}
return res
},
error => {
console.log('err' + error)
ElMessage.error(error.message)
return Promise.reject(error)
}
)
export default service
🐛 常见问题与解决方案
1. 分页查询问题
问题描述
前端发送分页请求时,后端返回"系统繁忙"错误
根本原因
- 参数类型不匹配(字符串 vs 布尔值)
- 缺少默认分页参数
解决方案
javascript
// 前端:确保参数类型正确
const queryParams = reactive({
pageNum: 1, // Number类型
pageSize: 10, // Number类型
usableStatus: null // null/Boolean类型
})
java
// 后端:添加默认构造函数
public ReportFormsPageParam() {
super();
this.setPageNum(1);
this.setPageSize(10);
this.setIsAsc("desc");
}
2. 状态管理最佳实践
组件内状态
javascript
// 使用 reactive 管理表单数据
const formData = reactive({
name: '',
status: null
})
// 使用 ref 管理简单状态
const loading = ref(false)
const visible = ref(false)
跨组件状态
javascript
// 使用 provide/inject
// 父组件
provide('userInfo', userInfo)
// 子组件
const userInfo = inject('userInfo')
📊 性能优化实践
1. 组件懒加载
javascript
// router/index.js
const routes = [
{
path: '/report/list',
component: () => import('@/components/report/management/ReportList.vue')
}
]
2. 表格虚拟滚动
vue
<!-- 大数据量表格优化 -->
<el-table-v2
:columns="columns"
:data="data"
:width="700"
:height="400"
fixed
/>
3. 请求防抖
javascript
import { debounce } from 'lodash-es'
// 搜索防抖
const handleSearch = debounce(() => {
getList()
}, 300)
🔍 调试与测试
1. 开发调试
javascript
// 开发环境日志
if (process.env.NODE_ENV === 'development') {
console.log('API请求参数:', params)
console.log('API响应数据:', response)
}
2. API测试
bash
# 使用PowerShell测试API
Invoke-WebRequest -Uri 'http://localhost:3000/defined-report/report/selectByPage' `
-Method POST `
-ContentType 'application/json' `
-Body '{"pageNum": 1, "pageSize": 10}'
📝 开发规范
1. 命名规范
- 文件命名: PascalCase (ReportList.vue)
- 组件名: PascalCase (ReportList)
- 方法名: camelCase (handleQuery)
- 常量名: UPPER_SNAKE_CASE (API_BASE_URL)
2. 代码注释
javascript
/**
* 获取报表列表数据
* @param {Object} params 查询参数
* @param {Number} params.pageNum 页码
* @param {Number} params.pageSize 每页大小
* @returns {Promise<Object>} 返回报表列表数据
*/
const getReportList = async (params) => {
// 实现逻辑
}
3. 错误处理
javascript
try {
const response = await apiCall()
// 成功处理
} catch (error) {
console.error('操作失败:', error)
ElMessage.error('操作失败,请稍后重试')
// 错误恢复逻辑
}
🚀 部署与发布
1. 构建配置
json
// package.json
{
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
}
}
2. 环境配置
javascript
// .env.development
VUE_APP_BASE_API = '/api'
VUE_APP_ENV = 'development'
// .env.production
VUE_APP_BASE_API = 'https://api.example.com'
VUE_APP_ENV = 'production'
📈 项目总结
成功经验
- 前后端类型对齐: 避免了大量的类型转换问题
- 组件化开发: 提高了代码复用性和维护性
- 统一错误处理: 提升了用户体验
- API层抽象: 便于接口管理和维护
改进建议
- 引入TypeScript: 提供更好的类型安全
- 添加单元测试: 保证代码质量
- 性能监控: 添加性能指标收集
- 文档完善: 建立完整的组件文档
技术债务
- 部分组件耦合度较高,需要进一步解耦
- 缺少统一的状态管理方案
- 错误边界处理不够完善
- 缺少自动化测试覆盖
🎯 最佳实践总结
- API优先设计: 前后端并行开发的基础
- 类型一致性: 减少运行时错误的关键
- 组件化思维: 提高开发效率的核心
- 错误处理: 用户体验的重要保障
- 性能意识: 从开发阶段就要考虑性能优化
- 文档驱动: 良好的文档是团队协作的基础
通过这个项目的实践,我们建立了一套完整的前端开发流程和规范,为后续项目提供了宝贵的经验和参考。