DBView 开发日志 ② — 侧边栏重构与 Navicat 风格化
日期: 2026-06-08
项目: DBView — Database Visual Explorer(数据库可视化工具)
状态: v1.0 功能完善与修复阶段
一、本期概要
在 v1.0 全部功能模块开发完成后,我们进入了一个重要的体验优化阶段。本期主要完成了两件大事:
- 侧边栏数据库树全面重构 — 从简陋的列表升级为 Navicat 风格的交互式树形结构
- 连接管理体验重塑 — 侧边栏内直接完成连接增删改查,无需跳转页面
同时修复了数据库树展开的一系列 Bug,确保基本交互流程的稳定性。
二、重构动机:从功能完备到体验顺滑
v1.0 阶段我们一口气完成了 10 大功能模块,但侧边栏的数据库树一直是个”短板”:
| 问题 | 影响 |
|---|---|
| 连接列表纯文本展示 | 无法直观区分数据库类型和连接状态 |
| 连接管理需要跳转页面 | 增删改查操作流程割裂 |
| 数据库节点无结构化展示 | 表/视图/存储过程混在一起 |
| 无右键菜单 | 所有操作依赖顶部按钮 |
| 无连接分组 | 连接多了以后难以管理 |
重构目标: 参考 Navicat 的侧边栏设计,让数据库树成为一个功能完整的操作中心。
三、Navicat 风格侧边栏设计
3.1 连接节点增强
每个连接节点现在展示:
1 | ┌──────────────────────────────────────┐ |
关键设计决策:
- 数据库类型徽章 — 每个连接旁显示彩色标签(MySQL 蓝、PostgreSQL 绿、SQLite 橙、Oracle 粉),一目了然
- 连接状态颜色 — 未连接时图标灰色(
#999),已连接时绿色(#52c41a) - 文件夹始终展示 — 表/视图/查询/用户四个文件夹始终可见,保持结构一致;存储过程和函数仅在存在时显示
- 数量标注 — 文件夹标题附带数量(如”表 (12)”),用户无需展开即可了解数据规模
3.2 连接分组管理
支持将连接归类到不同分组:
1 | // 分组节点 |
分组通过 connectionApi.listGroups() 加载,支持创建、重命名、删除分组。未分组的连接直接显示在根层级。
3.3 右键菜单体系
为每种节点类型设计了专属的右键菜单:
| 节点类型 | 菜单项 |
|---|---|
| 连接(未连接) | 连接 / 编辑连接 / 复制连接 / 删除连接 |
| 连接(已连接) | 断开连接 / 编辑连接 / 复制连接 / 删除连接 |
| 分组 | 新建连接 / 删除分组 |
| 数据库 | 新建查询 / 刷新 / 复制数据库名 |
| 表/视图 | 查看数据 / 查看结构 / 复制表名 / 在新查询中打开 |
| 存储过程/函数 | 查看定义 |
3.4 折叠/展开布局
侧边栏支持折叠为仅图标模式(36px 宽度),保留新建连接按钮,适合小屏幕或专注编辑的场景:
1 | // uiStore 中管理折叠状态 |
四、关键技术实现
4.1 DatabaseTree 组件重构
重构后的 DatabaseTree.tsx 从约 400 行增长到 943 行,是项目中最大的组件。核心架构:
1 | const DatabaseTree: React.FC = () => { |
4.2 异步加载链
数据库树的加载是一个多级异步链:
1 | 用户点击连接节点 |
4.3 关键数据结构
每个树节点通过 itemType 和 key 模式区分类型:
1 | // key 编码规则 |
4.4 用户列表接口
新增 getUsers() 接口,四种数据库驱动各自实现:
| 驱动 | 实现方式 |
|---|---|
| MySQL | SELECT User, Host FROM mysql.user |
| PostgreSQL | SELECT usename AS name FROM pg_catalog.pg_user |
| SQLite | 无用户概念,返回空数组 |
| Oracle | SELECT username AS name FROM dba_users |
1 | // 统一接口 |
4.5 treeData 缓存策略
一个值得注意的优化点:treeData 重建时保留已加载子节点。
当新建/编辑连接后,connections 状态变化会触发 treeData 重建。如果不做缓存,已展开的子树会被清空,用户需要重新展开所有节点。
1 | // 重建前索引所有已加载的 children |
五、Bug 修复实录
Bug 1:MySQL 系统库过滤导致空列表
症状: 连接 MySQL 后展开,数据库列表为空。
根因: MySQL 驱动 getDatabases() 过滤了系统库(information_schema、mysql、performance_schema、sys),但当用户只有系统库权限时,过滤后返回空列表。
修复: 有用户库时只显示用户库,没有则 fallback 显示全部(含系统库)。
1 | // mysql-driver.ts |
Bug 2:文件夹节点 key 解析错位
症状: 展开”表/视图/查询/用户”文件夹时 API 请求传入错误的连接 ID。
根因: 文件夹节点的 key 格式为 folder:${folderType}:${connId}:${schema},但 loadChildren 中解析时把 folderType 当成了 connId。
修复: 统一 key 解析逻辑,确保各段索引正确:
1 | // key format: folder:FOLDERTYPE:CONNID:SCHEMA[:TABLE] |
Bug 3:连接状态图标独立更新
问题: connectedIds 变化时,如果触发 treeData 全量重建,会清空 antd Tree 内部的 loadedKeys 缓存,导致已展开的节点重新加载。
修复: 将连接图标颜色更新拆为独立的 useEffect,仅替换连接节点的 icon 属性,不触及其他节点的 children:
1 | React.useEffect(() => { |
六、项目数据
| 指标 | 数值 |
|---|---|
| 总提交数 | 7 |
| 源文件数 | 46(.ts/.tsx) |
| 最大组件 | DatabaseTree.tsx(943 行) |
| 本期新增/修改文件 | 18 个 |
| 本期新增代码 | +961 行 |
| 本期删除代码 | -321 行 |
七、经验与反思
做得好的
- treeData 缓存策略 — 保留已加载子节点避免重建丢失,这个设计在后续多次 connections 变化中证明了价值
- 独立 icon 更新 effect — 将连接状态颜色更新与 treeData 重建分离,避免了 antd Tree 内部状态被意外清空
- key 编码规范 — 统一的 key 格式(
type:connId:schema:name)让解析逻辑清晰可维护
可以改进的
- DatabaseTree 组件过大 — 943 行承载了树渲染、异步加载、右键菜单、连接表单等太多职责,后续应拆分
- 异步加载缺少错误重试 — 网络波动时加载失败没有重试机制
- 连接自动展开时序 — 自动展开新连接时与 antd Tree 的
onLoadData存在竞态,需要更精细的协调
八、下期预告
下一期将深入讲解 数据库驱动架构设计与实现,包括:
DatabaseDriver接口的设计考量与演化- 四种数据库驱动的实现对比(MySQL / PostgreSQL / SQLite / Oracle)
- 查询取消机制的跨驱动实现
- 驱动测试策略
敬请期待!
DBView 开发日志系列 — 记录一个数据库可视化工具从 0 到 1 的完整历程