🔧 上下文系统
深入了解 Zhin.js 的上下文系统和依赖注入机制。
🎯 上下文系统概述
上下文系统是 Zhin.js 的核心特性,提供了强大的依赖注入功能,让插件可以声明式地使用各种服务。
🔧 注册上下文
基础注册
使用 register
函数注册上下文服务。
typescript
import { register } from 'zhin.js'
register({
name: 'database',
description: '数据库服务,提供数据查询和存储功能',
async mounted(plugin) {
// 初始化数据库连接
const db = await createDatabaseConnection()
plugin.logger.info('数据库已连接')
return db
},
async dispose(db) {
// 清理数据库连接
await db.close()
plugin.logger.info('数据库连接已关闭')
}
})
同步服务注册
对于不需要异步初始化的服务。
typescript
register({
name: 'cache',
description: '内存缓存服务',
mounted() {
return new Map()
},
dispose(cache) {
cache.clear()
}
})
无清理的服务
对于不需要清理的服务。
typescript
register({
name: 'config',
description: '配置服务',
mounted() {
return {
apiKey: process.env.API_KEY,
debug: process.env.DEBUG === 'true'
}
}
// 不需要 dispose 函数
})
🎯 使用上下文
单个依赖
使用单个上下文依赖。
typescript
import { useContext } from 'zhin.js'
useContext('database', (db) => {
// 数据库就绪后执行
addCommand(new MessageCommand('users')
.action(async () => {
const users = await db.query('SELECT * FROM users')
return `用户数量: ${users.length}`
})
)
})
多个依赖
使用多个上下文依赖。
typescript
useContext('database', 'cache', 'config', (db, cache, config) => {
// 所有依赖都就绪后执行
addCommand(new MessageCommand('stats')
.action(async () => {
const dbStats = await db.getStats()
const cacheStats = cache.size
const configInfo = config.debug ? '调试模式' : '生产模式'
return `数据库: ${dbStats}, 缓存: ${cacheStats}, 模式: ${configInfo}`
})
)
})
条件依赖
根据条件使用不同的依赖。
typescript
useContext('config', (config) => {
if (config.debug) {
useContext('debug-logger', (logger) => {
addCommand(new MessageCommand('debug')
.action(() => logger.getLogs())
)
})
} else {
useContext('production-logger', (logger) => {
addCommand(new MessageCommand('logs')
.action(() => logger.getRecentLogs())
)
})
}
})
🔄 依赖等待机制
智能等待
框架会自动等待所有依赖就绪。
typescript
// 即使 database 需要异步初始化,框架也会等待
useContext('database', 'cache', (db, cache) => {
// 这里保证 database 和 cache 都已完全初始化
console.log('所有依赖已就绪')
})
循环依赖检测
框架会自动检测和处理循环依赖。
typescript
// 服务A依赖服务B
register({
name: 'service-a',
async mounted() {
const serviceB = this.#use('service-b')
return new ServiceA(serviceB)
}
})
// 服务B依赖服务A
register({
name: 'service-b',
async mounted() {
const serviceA = this.#use('service-a')
return new ServiceB(serviceA)
}
})
// 框架会自动检测并处理这种循环依赖
🏗️ 服务设计模式
服务基类
创建可复用的服务基类。
typescript
abstract class BaseService {
protected logger: Logger
constructor(protected plugin: Plugin) {
this.logger = plugin.logger
}
abstract initialize(): Promise<void>
abstract cleanup(): Promise<void>
}
class DatabaseService extends BaseService {
private connection: any
async initialize() {
this.connection = await createConnection()
this.logger.info('数据库服务已初始化')
}
async cleanup() {
if (this.connection) {
await this.connection.close()
this.logger.info('数据库服务已清理')
}
}
async query(sql: string, params?: any[]) {
return this.connection.query(sql, params)
}
}
服务注册
注册服务实例。
typescript
register({
name: 'database',
description: '数据库服务',
async mounted(plugin) {
const service = new DatabaseService(plugin)
await service.initialize()
return service
},
async dispose(service) {
await service.cleanup()
}
})
🔧 高级用法
服务工厂
使用工厂模式创建服务。
typescript
class ServiceFactory {
static createDatabaseService(plugin: Plugin, config: DatabaseConfig) {
return new DatabaseService(plugin, config)
}
static createCacheService(plugin: Plugin, config: CacheConfig) {
return new CacheService(plugin, config)
}
}
register({
name: 'database',
async mounted(plugin) {
const config = plugin.getConfig('database')
return ServiceFactory.createDatabaseService(plugin, config)
}
})
服务组合
组合多个服务创建复合服务。
typescript
class UserService {
constructor(
private db: DatabaseService,
private cache: CacheService,
private logger: Logger
) {}
async getUser(id: string) {
// 先检查缓存
const cached = this.cache.get(`user:${id}`)
if (cached) return cached
// 从数据库获取
const user = await this.db.query('SELECT * FROM users WHERE id = ?', [id])
// 缓存结果
this.cache.set(`user:${id}`, user, 300000) // 5分钟
return user
}
}
register({
name: 'user-service',
async mounted(plugin) {
const db = plugin.getContext('database')
const cache = plugin.getContext('cache')
const logger = plugin.logger
return new UserService(db, cache, logger)
}
})
服务配置
为服务添加配置支持。
typescript
interface DatabaseConfig {
host: string
port: number
username: string
password: string
database: string
}
register({
name: 'database',
async mounted(plugin) {
const config: DatabaseConfig = {
host: process.env.DB_HOST || 'localhost',
port: parseInt(process.env.DB_PORT || '5432'),
username: process.env.DB_USER || 'postgres',
password: process.env.DB_PASSWORD || '',
database: process.env.DB_NAME || 'zhin'
}
const db = new DatabaseService(config)
await db.connect()
return db
}
})
🧪 测试上下文
模拟上下文
在测试中模拟上下文服务。
typescript
// tests/plugin.test.ts
import { describe, it, expect, beforeEach } from 'vitest'
describe('Plugin with Context', () => {
let mockDatabase: any
beforeEach(() => {
mockDatabase = {
query: vi.fn().mockResolvedValue([{ id: 1, name: 'test' }])
}
// 模拟上下文
register({
name: 'database',
mounted() {
return mockDatabase
}
})
})
it('should use database context', async () => {
useContext('database', (db) => {
expect(db).toBe(mockDatabase)
})
})
})
集成测试
测试上下文与插件的集成。
typescript
describe('Context Integration', () => {
it('should initialize all contexts', async () => {
const app = await createApp({
plugins: ['my-plugin']
})
await app.start()
// 验证上下文是否正确初始化
const db = app.getContext('database')
expect(db).toBeDefined()
expect(db.isConnected()).toBe(true)
})
})