第十二章:最佳实践
本章总结 VSDB 开发中的最佳实践,包括架构设计、性能优化、安全考虑和代码规范。
12.1 架构设计原则
进程隔离原则
graph TD
A["架构原则"] --> B["进程隔离"]
A --> C["异步优先"]
A --> D["错误边界"]
A --> E["状态持久化"]
B --> B1["数据库操作在 Worker"]
B --> B2["Worker 崩溃不影响 VSCode"]
C --> C1["IPC 消息异步"]
C --> C2["UI 不阻塞"]
D --> D1["错误分类"]
D --> D2["自动恢复"]
E --> E1["连接配置 JSON"]
E --> E2["密码 SecretStorage"]
关键原则总结
| 原则 | 实现 | 原因 |
|---|---|---|
| 进程隔离 | Worker 进程 | 数据库操作异常不影响 VSCode |
| 异步优先 | Promise + AsyncGenerator | UI 保持响应 |
| 错误边界 | try-catch + 错误分类 | 友好错误提示 |
| 状态持久化 | JSON + SecretStorage | 配置不丢失 |
12.2 性能优化
IPC 优化
// 1. 流式传输避免大消息
async *streamQuery(sql: string): AsyncGenerator<StreamChunk> {
for (let offset = 0; offset < rows.length; offset += 1000) {
yield { rows: rows.slice(offset, offset + 1000) };
}
}
// 2. 批量请求合并
async getFullSchema(connectionId: string): Promise<FullSchema> {
// 并行发送多个 schema 请求
const [databases, tables, columns] = await Promise.all([
this.sendRequest({ type: 'schema', payload: { schemaType: 'databases' } }),
this.sendRequest({ type: 'schema', payload: { schemaType: 'tables' } }),
this.sendRequest({ type: 'schema', payload: { schemaType: 'columns' } }),
]);
return { databases, tables, columns };
}
UI 渲染优化
// 1. 虚拟滚动
import { FixedSizeList } from 'react-window';
<FixedSizeList
height={400}
itemCount={rows.length}
itemSize={35}
>
{Row}
</FixedSizeList>
// 2. 惰性加载
async getChildren(element: TreeItem): Promise<TreeItem[]> {
// 仅在展开时加载子节点
if (element.collapsibleState === vscode.TreeItemCollapsibleState.Collapsed) {
return []; // 空数组,等待展开时加载
}
return await this.loadChildren(element);
}
// 3. 缓存策略
class SchemaCache {
private cache = new Map<string, { data: any; expireAt: number }>();
private ttl = 60000; // 1 分钟
get(key: string): any | null {
const entry = this.cache.get(key);
if (entry && entry.expireAt > Date.now()) {
return entry.data;
}
this.cache.delete(key);
return null;
}
set(key: string, data: any): void {
this.cache.set(key, {
data,
expireAt: Date.now() + this.ttl,
});
}
}
数据库查询优化
// 1. 连接池配置(PostgreSQL)
const pool = new Pool({
max: 5, // 限制连接数
connectionTimeoutMillis: 10000,
idleTimeoutMillis: 10000,
});
// 2. 查询超时
const queryQueue = new QueryQueue({
maxConcurrency: 5,
defaultTimeout: 30000, // 30 秒超时
});
// 3. 结果限制
const sql = `${baseSql} LIMIT 10000`; // 防止返回过多数据
12.3 安全考虑
密码存储
// 使用 VSCode SecretStorage API
export class Storage {
constructor(
private globalState: vscode.Memento,
private secrets: vscode.SecretStorage,
) {}
async savePassword(connectionId: string, password: string): Promise<void> {
await this.secrets.store(`vsdb.password.${connectionId}`, password);
}
async getPassword(connectionId: string): Promise<string | undefined> {
return await this.secrets.get(`vsdb.password.${connectionId}`);
}
}
SQL 注入防护
// 使用参数化查询
async query(sql: string, params?: any[]): Promise<QueryResult> {
// mysql2 和 pg 都支持参数化查询
await this.connection.execute(sql, params);
}
// 不要拼接 SQL
// 错误示例:
const sql = `SELECT * FROM users WHERE name = '${name}'`; // 危险!
// 正确示例:
const sql = `SELECT * FROM users WHERE name = ?`;
await this.connection.execute(sql, [name]);
连接字符串处理
// 安全解析 DATABASE_URL
parseConnectionString(url: string): ScannedConnection | null {
try {
const parsed = new URL(url);
// 解码用户名和密码
const username = decodeURIComponent(parsed.username || '');
const password = decodeURIComponent(parsed.password || '');
// 不暴露原始 URL
return {
username,
password,
// 不存储原始 URL
};
} catch {
return null;
}
}
12.4 错误处理规范
错误分类
type ErrorClass = 'connection' | 'syntax' | 'timeout' | 'permission' | 'unknown';
interface ClassifiedError {
errorClass: ErrorClass;
retryable: boolean;
userMessage: string;
}
const ERROR_MESSAGES: Record<ErrorClass, string> = {
connection: 'Connection failed. Check if database is running.',
syntax: 'SQL syntax error.',
timeout: 'Query timed out. Try again or simplify query.',
permission: 'Access denied. Check credentials.',
unknown: 'An unexpected error occurred.',
};
错误恢复策略
| 错误类型 | 策略 |
|---|---|
| connection | 重试 3 次,提示检查服务 |
| syntax | 不重试,显示错误位置 |
| timeout | 提示重试或简化查询 |
| permission | 不重试,提示检查权限 |
| Worker crash | 自动重启,恢复连接 |
用户友好提示
toUserMessage(error: unknown): string {
const message = this.extractMessage(error);
// 技术错误转换为用户友好提示
const mappings: Array<{ pattern: RegExp; message: string }> = [
{ pattern: /ECONNREFUSED/, message: 'Cannot connect to database. Is it running?' },
{ pattern: /ETIMEDOUT/, message: 'Connection timed out. Check network.' },
{ pattern: /syntax error/, message: 'SQL syntax error. Check your query.' },
{ pattern: /Access denied/, message: 'Permission denied. Check username/password.' },
];
for (const { pattern, message } of mappings) {
if (pattern.test(message)) {
return `VSDB: ${message}`;
}
}
return `VSDB: ${message}`;
}
12.5 代码规范
TypeScript 规范
// 1. 严格类型
// tsconfig.json
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
}
}
// 2. 接口优先
interface DbConnection {
id: string;
name: string;
// ...
}
// 3. 枚举替代字符串常量
type DatabaseType = 'mysql' | 'postgresql' | 'sqlite';
// 4. 明确的返回类型
async query(sql: string): Promise<QueryResult> {
// ...
}
命名规范
// 文件命名
connectionManager.ts // Manager 类
envParser.ts // Parser 类
mysqlDriver.ts // Driver 类
// 类命名
class ConnectionManager {}
class EnvParser {}
class MySqlDriver {}
// 接口命名
interface DbConnection {}
interface WorkerRequest {}
// 函数命名
async getConnection() {} // 获取
async addConnection() {} // 添加
async deleteConnection() {} // 删除
async executeQuery() {} // 执行
// 变量命名
const connectionId = '...'; // camelCase
const MAX_CONNECTIONS = 5; // 常量 UPPER_CASE
文档规范
/**
* IPC Manager for communicating with the DB Worker process.
*
* Responsibilities:
* - Spawn and manage Worker process lifecycle
* - Send WorkerRequest messages and match responses
* - Handle Worker crashes and auto-restart
*
* @example
* const ipc = new IpcManager({ workerScriptPath: './worker.js' });
* ipc.start();
* const response = await ipc.sendRequest({ type: 'query', ... });
*/
export class IpcManager {
// ...
}
12.6 测试规范
测试覆盖率要求
// vitest.config.ts
coverage: {
threshold: {
lines: 80,
functions: 80,
branches: 80,
},
}
测试命名
describe('IpcManager', () => {
describe('start', () => {
it('should start worker process', async () => {
// ...
});
it('should throw if worker script not found', async () => {
// ...
});
});
describe('sendRequest', () => {
it('should return response for valid request', async () => {
// ...
});
it('should timeout for long request', async () => {
// ...
});
});
});
测试结构
describe('Module', () => {
let instance: Module;
beforeEach(() => {
instance = new Module();
});
afterEach(() => {
instance.dispose();
});
describe('feature', () => {
it('should do something', async () => {
// 准备
const input = '...';
// 执行
const result = await instance.doSomething(input);
// 验证
expect(result).toBe('...');
});
});
});
12.7 小结
本章总结了 VSDB 开发的最佳实践:
| 主题 | 关键点 |
|---|---|
| 架构设计 | 进程隔离、异步优先、错误边界、状态持久化 |
| 性能优化 | 流式传输、虚拟滚动、缓存、并行请求 |
| 安全考虑 | SecretStorage、参数化查询、不暴露原始 URL |
| 错误处理 | 分类、友好提示、恢复策略 |
| 代码规范 | 严格类型、接口优先、命名规范 |
| 测试规范 | 覆盖率 80%、describe/it 结构 |
这些最佳实践确保 VSDB 的:
- 稳定性:Worker 崩溃不影响 VSCode
- 性能:大数据集流畅渲染
- 安全性:密码加密、防注入
- 可维护性:清晰代码结构、完整测试