我开源了一个 GrapesJS 插件

73 阅读3分钟

一、背景:为什么要做这个插件?

在做可视化编辑器(证书编辑器、模板编辑器、低代码页面编辑器)时,我大量使用了 GrapesJS

但在真实业务中,很快遇到了一个非常实际的问题:

Canvas 内的文本输入监听并不可靠

具体表现包括:

  • 中文输入法(IME)下:

    • input 事件触发不稳定
    • composition 阶段无法准确感知文本变化
  • GrapesJS 的 Canvas 是 iframe:

    • iframe reload 后监听全部失效
    • DOM 动态变化时无法自动重新绑定
  • 高频输入会导致:

    • 性能抖动
    • 无法做实时联动(预览、校验、联想)

而业务侧的诉求非常明确:

✅ 希望能稳定捕获:

  • 用户正在输入的文本(实时)
  • 用户完成输入的文本(提交)
  • 支持中文 / 日文 / 韩文输入法
  • 可配置节流

这就是 grapesjs-text-change 诞生的背景。


二、插件目标与能力设计

插件目标非常清晰:

🎯 为 GrapesJS 提供稳定、工程化的文本输入监听能力

核心能力:

能力说明
✅ IME 兼容支持中文 / 日文输入
✅ iframe 自动重绑定Canvas reload 后自动恢复
✅ 输入节流避免高频触发
✅ 标准事件输出对外统一事件接口
✅ TypeScript 支持类型安全
✅ 即插即用零侵入集成

三、使用效果展示

安装:

npm install grapesjs-text-change

集成:

import TextChangePlugin from 'grapesjs-text-change';

editor.use(TextChangePlugin, {
  throttle200,
});

editor.on('text:input'e => {
  console.log('实时输入:', e.text);
});

editor.on('text:commit'e => {
  console.log('输入完成:', e.text);
});

你可以:

  • 实时联动右侧预览
  • 做文本校验
  • 同步外部状态
  • 做多语言编辑联动

四、核心实现思路

1️⃣ iframe 自动绑定机制

GrapesJS 的 Canvas 是 iframe,不能只绑定一次。

插件内部监听:

editor.on('canvas:ready', bindFrame);
editor.on('canvas:frame:load', bindFrame);

每次 iframe 重建时:

  • 重新获取 contentDocument
  • 扫描 [contenteditable]
  • 自动绑定监听器

确保不会因为刷新导致监听失效。


2️⃣ IME 输入兼容处理

中文输入并不是简单的 input 事件。

需要监听:

  • compositionstart
  • compositionupdate
  • compositionend
  • input

插件内部维护一个状态机:

let composing = false;

el.addEventListener('compositionstart', () => composing = true);
el.addEventListener('compositionend', () => {
  composing = false;
  emitCommit();
});

el.addEventListener('input', () => {
  if (!composing) emitInput();
});

这样可以:

  • 避免拼音阶段误触发
  • 只在真正提交时触发 commit

3️⃣ 输入节流(Throttle)

高频输入如果不做节流:

  • 会触发大量业务逻辑
  • 性能下降明显

插件内使用可配置 throttle:

emitInput = throttle(fn, options.throttle);

用户可根据业务自由配置:

{ throttle: 100 }

4️⃣ 标准事件设计

插件对外只暴露两个稳定事件:

text:input   // 实时输入
text:commit  // 输入完成

统一事件格式:

{
  text: string;
  target: HTMLElement;
}

避免业务侧直接耦合 DOM。


五、工程化构建

📦 技术栈

  • TypeScript
  • tsup(打包)
  • ESM + CJS 双产物
  • 自动生成 d.ts

⚙️ tsup 配置示例

export default defineConfig({
  entry: ['src/index.ts'],
  format: ['esm''cjs'],
  dtstrue,
  sourcemaptrue,
  cleantrue,
});

打包:

pnpm build

📁 项目结构

grapesjs-text-change/
├── .github/
│   └── workflows/
│       ├── release.yml      # npm 发布 workflow
│       └── pages.yml        # GitHub Pages 部署 workflow
├── demo/                    # 在线演示
│   ├── index.html
│   ├── main.ts
│   ├── vite.config.ts
│   └── package.json
├── src/
│   ├── TextChangePlugin.ts  # 核心插件逻辑
│   └── index.ts             # 入口文件
├── package.json
├── tsup.config.ts
├── tsconfig.json
├── .gitignore
└── README.md

保持:

  • 职责拆分清晰
  • 可测试
  • 可扩展

六、自动化发布

通过 GitHub Actions + Changeset:

  • 自动版本管理
  • 自动发布到 npm
  • 自动生成 changelog

这保证了:

✅ 插件可持续维护 ✅ 发布过程稳定可靠 ✅ 降低人工成本


七、适合哪些场景?

这个插件特别适合:

✅ 可视化编辑器 ✅ 富文本编辑 ✅ 模板编辑器 ✅ 多语言编辑 ✅ 低代码平台 ✅ 在线证书 / 海报设计器

如果你在 GrapesJS 中:

  • 需要稳定监听文本变化
  • 需要支持中文输入
  • 不想重复踩坑

可以直接使用这个插件。


八、开源地址

欢迎 Star ⭐ / Issue / PR:

👉 GitHub github.com/xiayuguo/gr…

👉 NPM www.npmjs.com/package/gra…


九、结语

这个插件本质上是:

一次真实业务驱动的工程化抽象实践。

如果你也在做:

  • GrapesJS 二次开发
  • 编辑器工程
  • 低代码平台
  • 前端工程化

欢迎交流经验,一起打磨更好的工具生态 🚀