Batch Plugin for Knotx
npm install @knotx/plugins-batch批处理插件,为 KnotX 提供操作批处理功能。
``bash`
npm install @knotx/plugins-batch
Batch 插件为 KnotX 提供了操作批处理功能,能够将多个连续的节点操作合并为一个批处理操作,从而提高性能并减少历史记录条目。该插件通过 RxJS 的缓冲机制实现操作合并。
Batch 插件的核心实现原理:
1. 操作拦截:拦截节点操作管道,识别可批处理的操作
2. 缓冲机制:使用 RxJS 的 buffer 和 timer 实现时间窗口内的操作合并
3. 操作合并:将多个单独操作合并为一个批处理操作
4. 递归处理:支持嵌套批处理操作的扁平化处理
:提供基础插件架构和数据管理
- @knotx/decorators:提供装饰器支持
- rxjs:提供响应式编程支持$3
- @knotx/core 中的节点操作管道:自动集成到操作处理流程中API 文档
$3
#### Batch
批处理插件的主要类,继承自
BasePlugin。`typescript
export class Batch extends BasePlugin<'batch'> {
name = 'batch' as const
}
`$3
该插件自动工作,无需手动配置。它会:
1. 监听节点操作:自动拦截所有节点操作
2. 判断批处理条件:如果操作包含
draftId,则立即处理(不批处理)
3. 批量处理:将 100ms 内的操作合并为一个批处理操作
4. 递归扁平化:处理嵌套的批处理操作使用示例
$3
`typescript
import { Batch } from '@knotx/plugins-batch'const engine = new Engine({
plugins: [Batch],
})
// 批处理会自动工作,无需额外配置
`$3
`typescript
import { Batch } from '@knotx/plugins-batch'
import { Drag } from '@knotx/plugins-drag'
import { History } from '@knotx/plugins-history'const engine = new Engine({
plugins: [Batch, History, Drag],
})
// 批处理会优化历史记录和拖拽操作
`$3
`typescript
class BatchObserverPlugin extends BasePlugin {
@inject.nodesManager()
nodesManager!: DataManager @OnInit
init() {
// 监听节点操作
this.nodesManager.operations$.subscribe((operation) => {
if (operation.type === 'batch') {
console.log('批处理操作:', operation.operations.length, '个操作')
}
else {
console.log('单个操作:', operation.type)
}
})
}
}
`工作原理详解
$3
`typescript
// 源码示例(简化版)
transform: operations$ => pipe(
exhaustMap((firstOperation) => {
// 如果是草稿操作,立即处理
if ('draftId' in firstOperation) {
return of([firstOperation])
} // 否则等待缓冲窗口
return operations$.pipe(
startWith(firstOperation),
buffer(timer(100)), // 100ms 缓冲窗口
take(1),
)
}),
map(operations => ({
type: 'batch' as const,
operations: operations.flatMap(op =>
op.type === 'batch' ? op.operations : op
),
})),
)
`$3
1. 性能提升:减少频繁的重渲染和状态更新
2. 历史记录优化:将多个操作合并为一个历史记录条目
3. 用户体验:提供更流畅的操作感受
4. 资源节约:减少不必要的计算和内存分配
实际应用场景
$3
`typescript
// 拖拽过程中会产生大量位置更新操作
// 批处理会将这些操作合并为一个批处理操作engine.dispatchNodeOperation({
type: 'update',
data: { id: 'node1', position: { x: 100, y: 100 } },
})
engine.dispatchNodeOperation({
type: 'update',
data: { id: 'node1', position: { x: 101, y: 101 } },
})
engine.dispatchNodeOperation({
type: 'update',
data: { id: 'node1', position: { x: 102, y: 102 } },
})
// 以上操作会被合并为一个批处理操作
`$3
`typescript
// 批量添加多个节点
for (let i = 0; i < 10; i++) {
engine.dispatchNodeOperation({
type: 'add',
data: { id: node${i}, position: { x: i * 100, y: 100 } },
})
}// 这些操作会被合并为一个批处理操作
`$3
`typescript
// 复杂的操作序列
engine.dispatchNodeOperation({
type: 'add',
data: { id: 'node1', position: { x: 100, y: 100 } },
})engine.dispatchNodeOperation({
type: 'update',
data: { id: 'node1', data: { label: 'Updated' } },
})
engine.dispatchNodeOperation({
type: 'add',
data: { id: 'node2', position: { x: 200, y: 200 } },
})
// 这些操作会被合并为一个批处理操作
`性能影响
$3
1. 减少重渲染:批处理后的操作只触发一次重渲染
2. 优化历史记录:减少历史记录条目数量
3. 提升响应性:减少操作处理的延迟
$3
批处理使用固定的 100ms 缓冲窗口,这个时间在性能和响应性之间取得平衡:
- 太短:可能无法有效合并操作
- 太长:可能影响用户体验的实时性
与其他插件的交互
$3
`typescript
// 批处理会大大减少历史记录条目
const engine = new Engine({
plugins: [Batch, History],
})// 原本 100 个操作可能只产生 1 个历史记录条目
`$3
`typescript
// 拖拽操作会产生大量位置更新
// 批处理会将这些更新合并
const engine = new Engine({
plugins: [Batch, Drag],
})
`文件目录结构
`
packages/plugins-batch/
├── src/
│ ├── batch.ts # 主要实现文件
│ └── index.ts # 导出文件
├── dist/ # 构建输出目录
├── package.json # 包配置文件
├── build.config.ts # 构建配置
├── tsconfig.json # TypeScript 配置
├── eslint.config.mjs # ESLint 配置
└── CHANGELOG.md # 更新日志
`$3
- batch.ts:包含 Batch 插件的主要实现,包括操作拦截、缓冲机制和批处理逻辑
- index.ts:导出 Batch 类和相关类型定义
最佳实践
$3
1. 合理使用:批处理对于频繁的操作序列最有效
2. 配合其他插件:与 History 和 Drag 插件配合使用效果最佳
3. 监控效果:在开发阶段监控批处理的效果
$3
1. 操作日志:记录批处理前后的操作数量
2. 性能测试:对比启用/禁用批处理的性能差异
3. 内存监控:确保批处理不会导致内存泄漏
注意事项
1. 草稿操作:包含
draftId` 的操作不会被批处理MIT