npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@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 插件的核心实现原理:

  1. 操作拦截:拦截节点操作管道,识别可批处理的操作
  2. 缓冲机制:使用 RxJS 的 buffertimer 实现时间窗口内的操作合并
  3. 操作合并:将多个单独操作合并为一个批处理操作
  4. 递归处理:支持嵌套批处理操作的扁平化处理

依赖关系

核心依赖

  • @knotx/core:提供基础插件架构和数据管理
  • @knotx/decorators:提供装饰器支持
  • rxjs:提供响应式编程支持

自动依赖

  • @knotx/core 中的节点操作管道:自动集成到操作处理流程中

API 文档

主要类

Batch

批处理插件的主要类,继承自 BasePlugin

export class Batch extends BasePlugin<'batch'> {
  name = 'batch' as const
}

实现逻辑

该插件自动工作,无需手动配置。它会:

  1. 监听节点操作:自动拦截所有节点操作
  2. 判断批处理条件:如果操作包含 draftId,则立即处理(不批处理)
  3. 批量处理:将 100ms 内的操作合并为一个批处理操作
  4. 递归扁平化:处理嵌套的批处理操作

使用示例

基本用法

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
    ),
  })),
)

批处理优势

  1. 性能提升:减少频繁的重渲染和状态更新
  2. 历史记录优化:将多个操作合并为一个历史记录条目
  3. 用户体验:提供更流畅的操作感受
  4. 资源节约:减少不必要的计算和内存分配

实际应用场景

拖拽操作优化

// 拖拽过程中会产生大量位置更新操作
// 批处理会将这些操作合并为一个批处理操作

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 } },
})

// 这些操作会被合并为一个批处理操作

性能影响

优化效果

  1. 减少重渲染:批处理后的操作只触发一次重渲染
  2. 优化历史记录:减少历史记录条目数量
  3. 提升响应性:减少操作处理的延迟

缓冲时间调整

批处理使用固定的 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 类和相关类型定义

最佳实践

性能优化

  1. 合理使用:批处理对于频繁的操作序列最有效
  2. 配合其他插件:与 History 和 Drag 插件配合使用效果最佳
  3. 监控效果:在开发阶段监控批处理的效果

调试建议

  1. 操作日志:记录批处理前后的操作数量
  2. 性能测试:对比启用/禁用批处理的性能差异
  3. 内存监控:确保批处理不会导致内存泄漏

注意事项

  1. 草稿操作:包含 draftId 的操作不会被批处理
  2. 操作顺序:批处理保持操作的原始顺序
  3. 错误处理:批处理操作中的错误会影响整个批次
  4. 时间窗口:100ms 的缓冲时间是固定的,不可配置

许可证

MIT