一、背景:为什么要做这个插件?
在做可视化编辑器(证书编辑器、模板编辑器、低代码页面编辑器)时,我大量使用了 GrapesJS。
但在真实业务中,很快遇到了一个非常实际的问题:
❌ Canvas 内的文本输入监听并不可靠
具体表现包括:
-
中文输入法(IME)下:
input事件触发不稳定- composition 阶段无法准确感知文本变化
-
GrapesJS 的 Canvas 是 iframe:
- iframe reload 后监听全部失效
- DOM 动态变化时无法自动重新绑定
-
高频输入会导致:
- 性能抖动
- 无法做实时联动(预览、校验、联想)
而业务侧的诉求非常明确:
✅ 希望能稳定捕获:
- 用户正在输入的文本(实时)
- 用户完成输入的文本(提交)
- 支持中文 / 日文 / 韩文输入法
- 可配置节流
这就是 grapesjs-text-change 诞生的背景。
二、插件目标与能力设计
插件目标非常清晰:
🎯 为 GrapesJS 提供稳定、工程化的文本输入监听能力
核心能力:
| 能力 | 说明 |
|---|---|
| ✅ IME 兼容 | 支持中文 / 日文输入 |
| ✅ iframe 自动重绑定 | Canvas reload 后自动恢复 |
| ✅ 输入节流 | 避免高频触发 |
| ✅ 标准事件输出 | 对外统一事件接口 |
| ✅ TypeScript 支持 | 类型安全 |
| ✅ 即插即用 | 零侵入集成 |
三、使用效果展示
安装:
bash
npm install grapesjs-text-change
集成:
ts
import TextChangePlugin from 'grapesjs-text-change';
editor.use(TextChangePlugin, {
throttle: 200,
});
editor.on('text:input', e => {
console.log('实时输入:', e.text);
});
editor.on('text:commit', e => {
console.log('输入完成:', e.text);
});
你可以:
- 实时联动右侧预览
- 做文本校验
- 同步外部状态
- 做多语言编辑联动
四、核心实现思路
1️⃣ iframe 自动绑定机制
GrapesJS 的 Canvas 是 iframe,不能只绑定一次。
插件内部监听:
ts
editor.on('canvas:ready', bindFrame);
editor.on('canvas:frame:load', bindFrame);
每次 iframe 重建时:
- 重新获取
contentDocument - 扫描
[contenteditable] - 自动绑定监听器
确保不会因为刷新导致监听失效。
2️⃣ IME 输入兼容处理
中文输入并不是简单的 input 事件。
需要监听:
compositionstartcompositionupdatecompositionendinput
插件内部维护一个状态机:
ts
let composing = false;
el.addEventListener('compositionstart', () => composing = true);
el.addEventListener('compositionend', () => {
composing = false;
emitCommit();
});
el.addEventListener('input', () => {
if (!composing) emitInput();
});
这样可以:
- 避免拼音阶段误触发
- 只在真正提交时触发 commit
3️⃣ 输入节流(Throttle)
高频输入如果不做节流:
- 会触发大量业务逻辑
- 性能下降明显
插件内使用可配置 throttle:
ts
emitInput = throttle(fn, options.throttle);
用户可根据业务自由配置:
ts
{ throttle: 100 }
4️⃣ 标准事件设计
插件对外只暴露两个稳定事件:
ts
text:input // 实时输入
text:commit // 输入完成
统一事件格式:
ts
{
text: string;
target: HTMLElement;
}
避免业务侧直接耦合 DOM。
五、工程化构建
📦 技术栈
- TypeScript
- tsup(打包)
- ESM + CJS 双产物
- 自动生成 d.ts
⚙️ tsup 配置示例
ts
export default defineConfig({
entry: ['src/index.ts'],
format: ['esm', 'cjs'],
dts: true,
sourcemap: true,
clean: true,
});
打包:
bash
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
https://github.com/xiayuguo/grapesjs-text-change
👉 NPM
https://www.npmjs.com/package/grapesjs-text-change
九、结语
这个插件本质上是:
一次真实业务驱动的工程化抽象实践。
如果你也在做:
- GrapesJS 二次开发
- 编辑器工程
- 低代码平台
- 前端工程化
欢迎交流经验,一起打磨更好的工具生态 🚀