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