插件系统不能依赖 document.scripts 判断加载状态,因其只记录已解析的 script 标签,不反映 import()、eval、web worker 等动态注入逻辑;应通过编辑器运行时 api(如 tinymce 的 editor.plugins、monaco 的 getlanguages)或生命周期事件(pluginload、didinitialize)判断插件是否真正就绪。

插件系统不能依赖 Document.scripts 判断加载状态
Document.scripts 是只读的 HTMLCollection,它只记录已解析的 <script></script> 标签,不反映编辑器内部通过 import()、eval、Web Worker 或模块加载器注入的插件逻辑。很多编辑器(如 Monaco、CodeMirror 6)根本不在主文档 DOM 中插入 <script></script>,所以轮询 Document.scripts 或用 Array.from(Document.scripts).some(s => s.src.includes('my-plugin')) 永远得不到正确结果。
常见误操作包括:
- 用
while (Document.scripts.length === 0) {}等待脚本——直接阻塞主线程,且 DOM 脚本列表不会实时更新 - 检查
src是否含关键词——CDN 地址带 hash、插件被打包合并、内联脚本无src,全部失效 - 监听
<script></script>的load事件后就认为插件“已就绪”——但插件可能只是下载完成,注册逻辑尚未执行
必须通过编辑器实例的运行时 API 判断插件可用性
插件是否生效,取决于编辑器自身的注册表和激活状态,而不是脚本是否存在。不同编辑器暴露的判断方式差异很大:
- TinyMCE:检查
editor.plugins.myPlugin是否为真值,或调用editor.execCommand('myCustomCmd')看是否抛错 - Monaco Editor:查
monaco.editor.getLanguages().includes('mylang'),或确认monaco.languages.registerCompletionItemProvider已被调用 - CodeMirror 6:插件是
Extension对象,一旦传入EditorView.constructor({ extensions })即生效,无全局标识,需靠副作用(如自定义 facet、keymap)验证
更可靠的做法是监听编辑器提供的生命周期事件,例如 TinyMCE 的 PluginLoad,或 Monaco 的 didInitialize 回调,而不是监听 window 或 document 上的通用事件。
动态加载业务组件应走模块化路径,而非拼接 HTML 字符串
把业务组件当作 HTML 片段用 fetch + v-html 注入,看似简单,实则踩坑密集:脚本不执行、样式无隔离、XSS 风险高、事件绑定丢失。真正可扩展的方案是让业务组件以标准模块形式存在,并由编辑器统一调度。
推荐做法:
- 用
import('./plugins/finance-table.js')动态加载插件模块,返回 Promise,确保加载与初始化分离 - 插件模块导出一个安装函数,如
install(editor, options),由编辑器调用并传入当前实例 - 业务组件本身封装为 Vue/React 组件,通过编辑器提供的 UI 插槽(如 Monaco 的
registerEditorContribution)或浮层 API 渲染,不侵入编辑器 DOM - 避免在插件中直接操作
document.body或挂载全局变量;所有副作用应受编辑器生命周期管理(如onDidDispose时清理)
插件通信与沙箱边界必须显式约定
多个业务插件共存时,最易被忽略的是命名冲突和状态污染。比如两个插件都监听 Ctrl+Enter,或都修改 editor.content 导致覆盖。
解决方法不是靠“约定”,而是靠机制:
- 每个插件必须声明唯一
id,并在注册时传给编辑器(如tinymce.PluginManager.add('invoice-generator', ...)) - 插件间通信走编辑器内置事件总线(如
editor.dispatch('plugin:invoice-ready')),不直接调用对方方法 - 业务组件的数据上下文应来自编辑器传递的
context对象,而非读取localStorage或全局window变量 - 若需跨插件共享状态,应由编辑器提供受控的 store(如 CodeMirror 的
StateField),而非自行 new Store()
真正的扩展性不体现在“能加多少插件”,而在于“加了之后彼此不打架”。边界不清的插件系统,上线两周就会出现难以复现的竞态 bug。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











