@knotx/plugins-batch 0.4.11 → 0.4.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +286 -0
- package/README.md +286 -0
- package/package.json +8 -8
package/README.en.md
ADDED
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
# @knotx/plugins-batch
|
|
2
|
+
|
|
3
|
+
Batch plugin that provides operation batching functionality for KnotX.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @knotx/plugins-batch
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Overview
|
|
12
|
+
|
|
13
|
+
The Batch plugin provides operation batching functionality for KnotX, capable of merging multiple consecutive node operations into a single batch operation, thereby improving performance and reducing history entries. This plugin implements operation merging through RxJS buffering mechanisms.
|
|
14
|
+
|
|
15
|
+
## Implementation Principle
|
|
16
|
+
|
|
17
|
+
The core implementation principles of the Batch plugin:
|
|
18
|
+
|
|
19
|
+
1. **Operation Interception**: Intercepts node operation pipelines to identify batchable operations
|
|
20
|
+
2. **Buffering Mechanism**: Uses RxJS `buffer` and `timer` to merge operations within time windows
|
|
21
|
+
3. **Operation Merging**: Combines multiple individual operations into a single batch operation
|
|
22
|
+
4. **Recursive Processing**: Supports flattening of nested batch operations
|
|
23
|
+
|
|
24
|
+
## Dependencies
|
|
25
|
+
|
|
26
|
+
### Core Dependencies
|
|
27
|
+
- `@knotx/core`: Provides base plugin architecture and data management
|
|
28
|
+
- `@knotx/decorators`: Provides decorator support
|
|
29
|
+
- `rxjs`: Provides reactive programming support
|
|
30
|
+
|
|
31
|
+
### Automatic Dependencies
|
|
32
|
+
- Node operation pipeline in `@knotx/core`: Automatically integrates into operation processing flow
|
|
33
|
+
|
|
34
|
+
## API Documentation
|
|
35
|
+
|
|
36
|
+
### Main Classes
|
|
37
|
+
|
|
38
|
+
#### Batch
|
|
39
|
+
|
|
40
|
+
The main class of the Batch plugin, extending `BasePlugin`.
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
export class Batch extends BasePlugin<'batch'> {
|
|
44
|
+
name = 'batch' as const
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Implementation Logic
|
|
49
|
+
|
|
50
|
+
This plugin works automatically without manual configuration. It will:
|
|
51
|
+
|
|
52
|
+
1. **Monitor Node Operations**: Automatically intercepts all node operations
|
|
53
|
+
2. **Determine Batch Conditions**: If operation contains `draftId`, process immediately (no batching)
|
|
54
|
+
3. **Batch Processing**: Merge operations within 100ms into a single batch operation
|
|
55
|
+
4. **Recursive Flattening**: Handle nested batch operations
|
|
56
|
+
|
|
57
|
+
## Usage Examples
|
|
58
|
+
|
|
59
|
+
### Basic Usage
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
import { Batch } from '@knotx/plugins-batch'
|
|
63
|
+
|
|
64
|
+
const engine = new Engine({
|
|
65
|
+
plugins: [Batch],
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
// Batching works automatically without additional configuration
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Integration with Other Plugins
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
import { Batch } from '@knotx/plugins-batch'
|
|
75
|
+
import { Drag } from '@knotx/plugins-drag'
|
|
76
|
+
import { History } from '@knotx/plugins-history'
|
|
77
|
+
|
|
78
|
+
const engine = new Engine({
|
|
79
|
+
plugins: [Batch, History, Drag],
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
// Batching will optimize history records and drag operations
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Observing Batch Effects
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
class BatchObserverPlugin extends BasePlugin {
|
|
89
|
+
@inject.nodesManager()
|
|
90
|
+
nodesManager!: DataManager<Node>
|
|
91
|
+
|
|
92
|
+
@OnInit
|
|
93
|
+
init() {
|
|
94
|
+
// Monitor node operations
|
|
95
|
+
this.nodesManager.operations$.subscribe((operation) => {
|
|
96
|
+
if (operation.type === 'batch') {
|
|
97
|
+
console.log('Batch operation:', operation.operations.length, 'operations')
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
console.log('Single operation:', operation.type)
|
|
101
|
+
}
|
|
102
|
+
})
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Detailed Working Principle
|
|
108
|
+
|
|
109
|
+
### Operation Type Determination
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
// Source code example (simplified)
|
|
113
|
+
transform: operations$ => pipe(
|
|
114
|
+
exhaustMap((firstOperation) => {
|
|
115
|
+
// If draft operation, process immediately
|
|
116
|
+
if ('draftId' in firstOperation) {
|
|
117
|
+
return of([firstOperation])
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Otherwise wait for buffer window
|
|
121
|
+
return operations$.pipe(
|
|
122
|
+
startWith(firstOperation),
|
|
123
|
+
buffer(timer(100)), // 100ms buffer window
|
|
124
|
+
take(1),
|
|
125
|
+
)
|
|
126
|
+
}),
|
|
127
|
+
map(operations => ({
|
|
128
|
+
type: 'batch' as const,
|
|
129
|
+
operations: operations.flatMap(op =>
|
|
130
|
+
op.type === 'batch' ? op.operations : op
|
|
131
|
+
),
|
|
132
|
+
})),
|
|
133
|
+
)
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Batch Advantages
|
|
137
|
+
|
|
138
|
+
1. **Performance Improvement**: Reduces frequent re-renders and state updates
|
|
139
|
+
2. **History Optimization**: Merges multiple operations into a single history entry
|
|
140
|
+
3. **User Experience**: Provides smoother operation feel
|
|
141
|
+
4. **Resource Savings**: Reduces unnecessary computation and memory allocation
|
|
142
|
+
|
|
143
|
+
## Real-world Application Scenarios
|
|
144
|
+
|
|
145
|
+
### Drag Operation Optimization
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
// Dragging produces many position update operations
|
|
149
|
+
// Batching will merge these operations into a single batch operation
|
|
150
|
+
|
|
151
|
+
engine.dispatchNodeOperation({
|
|
152
|
+
type: 'update',
|
|
153
|
+
data: { id: 'node1', position: { x: 100, y: 100 } },
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
engine.dispatchNodeOperation({
|
|
157
|
+
type: 'update',
|
|
158
|
+
data: { id: 'node1', position: { x: 101, y: 101 } },
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
engine.dispatchNodeOperation({
|
|
162
|
+
type: 'update',
|
|
163
|
+
data: { id: 'node1', position: { x: 102, y: 102 } },
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
// Above operations will be merged into a single batch operation
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Batch Adding Nodes
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
// Batch adding multiple nodes
|
|
173
|
+
for (let i = 0; i < 10; i++) {
|
|
174
|
+
engine.dispatchNodeOperation({
|
|
175
|
+
type: 'add',
|
|
176
|
+
data: { id: `node${i}`, position: { x: i * 100, y: 100 } },
|
|
177
|
+
})
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// These operations will be merged into a single batch operation
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Complex Operation Sequences
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
// Complex operation sequences
|
|
187
|
+
engine.dispatchNodeOperation({
|
|
188
|
+
type: 'add',
|
|
189
|
+
data: { id: 'node1', position: { x: 100, y: 100 } },
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
engine.dispatchNodeOperation({
|
|
193
|
+
type: 'update',
|
|
194
|
+
data: { id: 'node1', data: { label: 'Updated' } },
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
engine.dispatchNodeOperation({
|
|
198
|
+
type: 'add',
|
|
199
|
+
data: { id: 'node2', position: { x: 200, y: 200 } },
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
// These operations will be merged into a single batch operation
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## Performance Impact
|
|
206
|
+
|
|
207
|
+
### Optimization Effects
|
|
208
|
+
|
|
209
|
+
1. **Reduced Re-renders**: Batched operations only trigger one re-render
|
|
210
|
+
2. **Optimized History**: Reduces number of history entries
|
|
211
|
+
3. **Improved Responsiveness**: Reduces operation processing latency
|
|
212
|
+
|
|
213
|
+
### Buffer Time Adjustment
|
|
214
|
+
|
|
215
|
+
Batching uses a fixed 100ms buffer window, which balances performance and responsiveness:
|
|
216
|
+
|
|
217
|
+
- **Too Short**: May not effectively merge operations
|
|
218
|
+
- **Too Long**: May affect real-time user experience
|
|
219
|
+
|
|
220
|
+
## Interaction with Other Plugins
|
|
221
|
+
|
|
222
|
+
### History Plugin
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
// Batching will significantly reduce history entries
|
|
226
|
+
const engine = new Engine({
|
|
227
|
+
plugins: [Batch, History],
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
// Originally 100 operations might produce only 1 history entry
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Drag Plugin
|
|
234
|
+
|
|
235
|
+
```typescript
|
|
236
|
+
// Drag operations produce many position updates
|
|
237
|
+
// Batching will merge these updates
|
|
238
|
+
const engine = new Engine({
|
|
239
|
+
plugins: [Batch, Drag],
|
|
240
|
+
})
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
## File Directory Structure
|
|
244
|
+
|
|
245
|
+
```
|
|
246
|
+
packages/plugins-batch/
|
|
247
|
+
├── src/
|
|
248
|
+
│ ├── batch.ts # Main implementation file
|
|
249
|
+
│ └── index.ts # Export file
|
|
250
|
+
├── dist/ # Build output directory
|
|
251
|
+
├── package.json # Package configuration
|
|
252
|
+
├── build.config.ts # Build configuration
|
|
253
|
+
├── tsconfig.json # TypeScript configuration
|
|
254
|
+
├── eslint.config.mjs # ESLint configuration
|
|
255
|
+
└── CHANGELOG.md # Changelog
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Core Files Description
|
|
259
|
+
|
|
260
|
+
- **batch.ts**: Contains the main implementation of the Batch plugin, including operation interception, buffering mechanism, and batch processing logic
|
|
261
|
+
- **index.ts**: Exports the Batch class and related type definitions
|
|
262
|
+
|
|
263
|
+
## Best Practices
|
|
264
|
+
|
|
265
|
+
### Performance Optimization
|
|
266
|
+
|
|
267
|
+
1. **Reasonable Usage**: Batching is most effective for frequent operation sequences
|
|
268
|
+
2. **Combine with Other Plugins**: Best results when used with History and Drag plugins
|
|
269
|
+
3. **Monitor Effects**: Monitor batching effects during development
|
|
270
|
+
|
|
271
|
+
### Debugging Recommendations
|
|
272
|
+
|
|
273
|
+
1. **Operation Logging**: Log operation counts before and after batching
|
|
274
|
+
2. **Performance Testing**: Compare performance with/without batching enabled
|
|
275
|
+
3. **Memory Monitoring**: Ensure batching doesn't cause memory leaks
|
|
276
|
+
|
|
277
|
+
## Notes
|
|
278
|
+
|
|
279
|
+
1. **Draft Operations**: Operations containing `draftId` will not be batched
|
|
280
|
+
2. **Operation Order**: Batching preserves the original order of operations
|
|
281
|
+
3. **Error Handling**: Errors in batch operations affect the entire batch
|
|
282
|
+
4. **Time Window**: The 100ms buffer time is fixed and not configurable
|
|
283
|
+
|
|
284
|
+
## License
|
|
285
|
+
|
|
286
|
+
MIT
|
package/README.md
ADDED
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
# @knotx/plugins-batch
|
|
2
|
+
|
|
3
|
+
批处理插件,为 KnotX 提供操作批处理功能。
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @knotx/plugins-batch
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## 概述
|
|
12
|
+
|
|
13
|
+
Batch 插件为 KnotX 提供了操作批处理功能,能够将多个连续的节点操作合并为一个批处理操作,从而提高性能并减少历史记录条目。该插件通过 RxJS 的缓冲机制实现操作合并。
|
|
14
|
+
|
|
15
|
+
## 实现原理
|
|
16
|
+
|
|
17
|
+
Batch 插件的核心实现原理:
|
|
18
|
+
|
|
19
|
+
1. **操作拦截**:拦截节点操作管道,识别可批处理的操作
|
|
20
|
+
2. **缓冲机制**:使用 RxJS 的 `buffer` 和 `timer` 实现时间窗口内的操作合并
|
|
21
|
+
3. **操作合并**:将多个单独操作合并为一个批处理操作
|
|
22
|
+
4. **递归处理**:支持嵌套批处理操作的扁平化处理
|
|
23
|
+
|
|
24
|
+
## 依赖关系
|
|
25
|
+
|
|
26
|
+
### 核心依赖
|
|
27
|
+
- `@knotx/core`:提供基础插件架构和数据管理
|
|
28
|
+
- `@knotx/decorators`:提供装饰器支持
|
|
29
|
+
- `rxjs`:提供响应式编程支持
|
|
30
|
+
|
|
31
|
+
### 自动依赖
|
|
32
|
+
- `@knotx/core` 中的节点操作管道:自动集成到操作处理流程中
|
|
33
|
+
|
|
34
|
+
## API 文档
|
|
35
|
+
|
|
36
|
+
### 主要类
|
|
37
|
+
|
|
38
|
+
#### Batch
|
|
39
|
+
|
|
40
|
+
批处理插件的主要类,继承自 `BasePlugin`。
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
export class Batch extends BasePlugin<'batch'> {
|
|
44
|
+
name = 'batch' as const
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### 实现逻辑
|
|
49
|
+
|
|
50
|
+
该插件自动工作,无需手动配置。它会:
|
|
51
|
+
|
|
52
|
+
1. **监听节点操作**:自动拦截所有节点操作
|
|
53
|
+
2. **判断批处理条件**:如果操作包含 `draftId`,则立即处理(不批处理)
|
|
54
|
+
3. **批量处理**:将 100ms 内的操作合并为一个批处理操作
|
|
55
|
+
4. **递归扁平化**:处理嵌套的批处理操作
|
|
56
|
+
|
|
57
|
+
## 使用示例
|
|
58
|
+
|
|
59
|
+
### 基本用法
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
import { Batch } from '@knotx/plugins-batch'
|
|
63
|
+
|
|
64
|
+
const engine = new Engine({
|
|
65
|
+
plugins: [Batch],
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
// 批处理会自动工作,无需额外配置
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### 与其他插件集成
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
import { Batch } from '@knotx/plugins-batch'
|
|
75
|
+
import { Drag } from '@knotx/plugins-drag'
|
|
76
|
+
import { History } from '@knotx/plugins-history'
|
|
77
|
+
|
|
78
|
+
const engine = new Engine({
|
|
79
|
+
plugins: [Batch, History, Drag],
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
// 批处理会优化历史记录和拖拽操作
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### 观察批处理效果
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
class BatchObserverPlugin extends BasePlugin {
|
|
89
|
+
@inject.nodesManager()
|
|
90
|
+
nodesManager!: DataManager<Node>
|
|
91
|
+
|
|
92
|
+
@OnInit
|
|
93
|
+
init() {
|
|
94
|
+
// 监听节点操作
|
|
95
|
+
this.nodesManager.operations$.subscribe((operation) => {
|
|
96
|
+
if (operation.type === 'batch') {
|
|
97
|
+
console.log('批处理操作:', operation.operations.length, '个操作')
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
console.log('单个操作:', operation.type)
|
|
101
|
+
}
|
|
102
|
+
})
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## 工作原理详解
|
|
108
|
+
|
|
109
|
+
### 操作类型判断
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
// 源码示例(简化版)
|
|
113
|
+
transform: operations$ => pipe(
|
|
114
|
+
exhaustMap((firstOperation) => {
|
|
115
|
+
// 如果是草稿操作,立即处理
|
|
116
|
+
if ('draftId' in firstOperation) {
|
|
117
|
+
return of([firstOperation])
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// 否则等待缓冲窗口
|
|
121
|
+
return operations$.pipe(
|
|
122
|
+
startWith(firstOperation),
|
|
123
|
+
buffer(timer(100)), // 100ms 缓冲窗口
|
|
124
|
+
take(1),
|
|
125
|
+
)
|
|
126
|
+
}),
|
|
127
|
+
map(operations => ({
|
|
128
|
+
type: 'batch' as const,
|
|
129
|
+
operations: operations.flatMap(op =>
|
|
130
|
+
op.type === 'batch' ? op.operations : op
|
|
131
|
+
),
|
|
132
|
+
})),
|
|
133
|
+
)
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### 批处理优势
|
|
137
|
+
|
|
138
|
+
1. **性能提升**:减少频繁的重渲染和状态更新
|
|
139
|
+
2. **历史记录优化**:将多个操作合并为一个历史记录条目
|
|
140
|
+
3. **用户体验**:提供更流畅的操作感受
|
|
141
|
+
4. **资源节约**:减少不必要的计算和内存分配
|
|
142
|
+
|
|
143
|
+
## 实际应用场景
|
|
144
|
+
|
|
145
|
+
### 拖拽操作优化
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
// 拖拽过程中会产生大量位置更新操作
|
|
149
|
+
// 批处理会将这些操作合并为一个批处理操作
|
|
150
|
+
|
|
151
|
+
engine.dispatchNodeOperation({
|
|
152
|
+
type: 'update',
|
|
153
|
+
data: { id: 'node1', position: { x: 100, y: 100 } },
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
engine.dispatchNodeOperation({
|
|
157
|
+
type: 'update',
|
|
158
|
+
data: { id: 'node1', position: { x: 101, y: 101 } },
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
engine.dispatchNodeOperation({
|
|
162
|
+
type: 'update',
|
|
163
|
+
data: { id: 'node1', position: { x: 102, y: 102 } },
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
// 以上操作会被合并为一个批处理操作
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### 批量添加节点
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
// 批量添加多个节点
|
|
173
|
+
for (let i = 0; i < 10; i++) {
|
|
174
|
+
engine.dispatchNodeOperation({
|
|
175
|
+
type: 'add',
|
|
176
|
+
data: { id: `node${i}`, position: { x: i * 100, y: 100 } },
|
|
177
|
+
})
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// 这些操作会被合并为一个批处理操作
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### 复杂操作序列
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
// 复杂的操作序列
|
|
187
|
+
engine.dispatchNodeOperation({
|
|
188
|
+
type: 'add',
|
|
189
|
+
data: { id: 'node1', position: { x: 100, y: 100 } },
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
engine.dispatchNodeOperation({
|
|
193
|
+
type: 'update',
|
|
194
|
+
data: { id: 'node1', data: { label: 'Updated' } },
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
engine.dispatchNodeOperation({
|
|
198
|
+
type: 'add',
|
|
199
|
+
data: { id: 'node2', position: { x: 200, y: 200 } },
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
// 这些操作会被合并为一个批处理操作
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## 性能影响
|
|
206
|
+
|
|
207
|
+
### 优化效果
|
|
208
|
+
|
|
209
|
+
1. **减少重渲染**:批处理后的操作只触发一次重渲染
|
|
210
|
+
2. **优化历史记录**:减少历史记录条目数量
|
|
211
|
+
3. **提升响应性**:减少操作处理的延迟
|
|
212
|
+
|
|
213
|
+
### 缓冲时间调整
|
|
214
|
+
|
|
215
|
+
批处理使用固定的 100ms 缓冲窗口,这个时间在性能和响应性之间取得平衡:
|
|
216
|
+
|
|
217
|
+
- **太短**:可能无法有效合并操作
|
|
218
|
+
- **太长**:可能影响用户体验的实时性
|
|
219
|
+
|
|
220
|
+
## 与其他插件的交互
|
|
221
|
+
|
|
222
|
+
### History 插件
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
// 批处理会大大减少历史记录条目
|
|
226
|
+
const engine = new Engine({
|
|
227
|
+
plugins: [Batch, History],
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
// 原本 100 个操作可能只产生 1 个历史记录条目
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Drag 插件
|
|
234
|
+
|
|
235
|
+
```typescript
|
|
236
|
+
// 拖拽操作会产生大量位置更新
|
|
237
|
+
// 批处理会将这些更新合并
|
|
238
|
+
const engine = new Engine({
|
|
239
|
+
plugins: [Batch, Drag],
|
|
240
|
+
})
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
## 文件目录结构
|
|
244
|
+
|
|
245
|
+
```
|
|
246
|
+
packages/plugins-batch/
|
|
247
|
+
├── src/
|
|
248
|
+
│ ├── batch.ts # 主要实现文件
|
|
249
|
+
│ └── index.ts # 导出文件
|
|
250
|
+
├── dist/ # 构建输出目录
|
|
251
|
+
├── package.json # 包配置文件
|
|
252
|
+
├── build.config.ts # 构建配置
|
|
253
|
+
├── tsconfig.json # TypeScript 配置
|
|
254
|
+
├── eslint.config.mjs # ESLint 配置
|
|
255
|
+
└── CHANGELOG.md # 更新日志
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### 核心文件说明
|
|
259
|
+
|
|
260
|
+
- **batch.ts**:包含 Batch 插件的主要实现,包括操作拦截、缓冲机制和批处理逻辑
|
|
261
|
+
- **index.ts**:导出 Batch 类和相关类型定义
|
|
262
|
+
|
|
263
|
+
## 最佳实践
|
|
264
|
+
|
|
265
|
+
### 性能优化
|
|
266
|
+
|
|
267
|
+
1. **合理使用**:批处理对于频繁的操作序列最有效
|
|
268
|
+
2. **配合其他插件**:与 History 和 Drag 插件配合使用效果最佳
|
|
269
|
+
3. **监控效果**:在开发阶段监控批处理的效果
|
|
270
|
+
|
|
271
|
+
### 调试建议
|
|
272
|
+
|
|
273
|
+
1. **操作日志**:记录批处理前后的操作数量
|
|
274
|
+
2. **性能测试**:对比启用/禁用批处理的性能差异
|
|
275
|
+
3. **内存监控**:确保批处理不会导致内存泄漏
|
|
276
|
+
|
|
277
|
+
## 注意事项
|
|
278
|
+
|
|
279
|
+
1. **草稿操作**:包含 `draftId` 的操作不会被批处理
|
|
280
|
+
2. **操作顺序**:批处理保持操作的原始顺序
|
|
281
|
+
3. **错误处理**:批处理操作中的错误会影响整个批次
|
|
282
|
+
4. **时间窗口**:100ms 的缓冲时间是固定的,不可配置
|
|
283
|
+
|
|
284
|
+
## 许可证
|
|
285
|
+
|
|
286
|
+
MIT
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@knotx/plugins-batch",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.13",
|
|
4
4
|
"description": "Batch Plugin for Knotx",
|
|
5
5
|
"author": "boenfu",
|
|
6
6
|
"license": "MIT",
|
|
@@ -28,18 +28,18 @@
|
|
|
28
28
|
"dist"
|
|
29
29
|
],
|
|
30
30
|
"peerDependencies": {
|
|
31
|
-
"@knotx/jsx": "0.4.
|
|
31
|
+
"@knotx/jsx": "0.4.13"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"rxjs": "^7.8.1",
|
|
35
|
-
"@knotx/core": "0.4.
|
|
36
|
-
"@knotx/decorators": "0.4.
|
|
35
|
+
"@knotx/core": "0.4.13",
|
|
36
|
+
"@knotx/decorators": "0.4.13"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"@knotx/build-config": "0.4.
|
|
40
|
-
"@knotx/eslint-config": "0.4.
|
|
41
|
-
"@knotx/jsx": "0.4.
|
|
42
|
-
"@knotx/typescript-config": "0.4.
|
|
39
|
+
"@knotx/build-config": "0.4.13",
|
|
40
|
+
"@knotx/eslint-config": "0.4.13",
|
|
41
|
+
"@knotx/jsx": "0.4.13",
|
|
42
|
+
"@knotx/typescript-config": "0.4.13"
|
|
43
43
|
},
|
|
44
44
|
"scripts": {
|
|
45
45
|
"build": "unbuild",
|