核心在于用data-属性驱动行为而非if判断,通过data-action等元信息实现逻辑解耦;配合slot内容投影、is属性复用原生标签及事件委托,构建真正可插拔的按钮系统。

用 data- 属性驱动按钮行为,别写 if 判断
按钮是否“可插拔”,核心不在标签本身,而在它携带的元信息能否被 JS 一致读取并触发对应逻辑。直接在 <button></button> 上加 data-action、data-target、data-config 这类属性,比写一堆 if (id === 'export-btn') {...} 更可持续。
常见错误:把配置塞进 id 或 class 里,比如 id="btn-export-csv"——这会让 JS 不得不做字符串解析,且无法携带结构化数据。
-
dataset自动转驼峰,但值永远是字符串:data-api-endpoint="/v2/users"→btn.dataset.apiEndpoint是字符串,需手动JSON.parse()或parseInt() - 避免重复注册事件:用委托监听更稳妥,比如给
body绑定click,再通过event.target.matches('[data-action]')判断 - 不要依赖属性存在即“启用”:加个
data-enabled="false"显式控制开关,比删属性或设空值更可靠
用 slot + 自定义元素封装按钮容器,而非拼 HTML 字符串
如果按钮需要动态插入图标、徽标、加载状态,硬拼 innerHTML 会丢失事件绑定、破坏 SSR 兼容性、引入 XSS 风险。真正可插拔的按钮容器应支持内容投影。
关键前提:必须调用 this.attachShadow({ mode: 'open' }),否则 <slot></slot> 在自定义元素里只是文本节点。
- 默认插槽接收任意子内容:
<my-button><span>提交</span><icon-spin slot="loading"></icon-spin></my-button> - 具名插槽分离关注点:
<slot name="icon"></slot>和<slot name="label"></slot>让主题或 i18n 插件能单独替换某一部分 - 别在 Shadow DOM 里用
document.querySelector:改用this.shadowRoot.querySelector,否则查不到
用 is 属性复用原生按钮,绕过 customElements.define 时机问题
在 CMS 输出页、第三方脚本未加载完、或 SSR 渲染场景下,你可能没法等 customElements.define() 执行完再写按钮。这时 is 是唯一合法的降级方案。
将调研报告、方案对比、数据分析、需求梳理、会议纪要、设计说明、代码评审、学习笔记等任务产物输出为可在浏览器直接打开的单文件 HTML,替代 Markdown 作为最终交付物。Markdown 在信息密度、视觉清晰度和可分享性上已跟不上 AI 产物的丰富度,HTML 能同时承载排版、表格、SVG 等。
注意:只对可扩展的内置标签有效(button、input、article 等),且类必须继承对应原生构造器。
- HTML 写法:
<button is="smart-button" data-mode="async">保存</button> - JS 注册必须带
{ extends: 'button' }:customElements.define('smart-button', SmartButton, { extends: 'button' }) - 不能用
is挂在<div is="..."> 上——浏览器会忽略,因为 <code>div不支持扩展 - 服务端渲染时,
is属性会被保留,JS 加载后自动升级,无需额外标记或占位逻辑 - TinyMCE 场景:检查
editor.plugins['my-button-plugin']是否为函数,或执行editor.execCommand('insertMyButton')看是否报错 - 自定义按钮系统:暴露
buttonRegistry.has('export-to-pdf')方法,由插件加载后主动注册 - 监听生命周期事件更可靠:
editor.on('PluginLoad', e => { if (e.name === 'my-button') initMyButton() }) - 加载失败时,
import('./plugins/export-btn.js').catch(...)必须显式 fallback,不能静默吞掉错误
动态加载插件时,别查 Document.scripts,查编辑器/按钮实例状态
很多团队试图靠轮询 Document.scripts 判断按钮插件是否加载完成,这是典型误操作。现代模块加载(import()、Web Worker、内联脚本)根本不会往 Document.scripts 里写记录。
真正有效的判断依据,永远来自运行时实例本身。
插件是否生效,取决于它是否向运行时注册了能力,而不是它对应的 JS 文件有没有被下载完。这个边界模糊点,是多数“可插拔”系统半途崩溃的根源。










