插件设计需提供统一函数式入口(如init()),返回含生命周期方法的实例;配置须深克隆并冻结默认值;DOM挂载点由用户指定且兼容多形态;扩展依赖事件系统与钩子函数,执行前校验类型并绑定this。

插件设计必须暴露明确的初始化入口
用户调用你的 JS 插件时,最怕找不到「从哪开始」。不要依赖全局变量自动初始化,也不该要求用户手动 new 一堆类。统一用一个函数式入口,比如 init() 或 create(),且只接受一个配置对象参数。
-
init()应返回实例对象(含destroy()、update()等方法),方便链式控制和生命周期管理 - 避免在入口里执行 DOM 查询或副作用逻辑——延迟到首次
render()或mount()时再触发 - 若支持多实例,确保每个实例状态隔离,别共用闭包内的数组或对象引用
扩展能力靠事件系统 + 钩子函数,不是靠继承
用户想加功能,不该去改你的源码或重写类。提供标准事件(如 "beforeOpen"、"afterRender")和可覆盖的钩子(如 onOpen、onClose 配置项),比留出 extend() 方法更可控、更易调试。
- 事件用
dispatchEvent()+CustomEvent,不依赖第三方 EventEmitter - 钩子函数执行前加
typeof hook === 'function'判断,避免未定义报错中断流程 - 钩子执行上下文应绑定实例(
this指向当前插件对象),而不是调用时的 window
const event = new CustomEvent('beforeOpen', {
detail: { target: this.element },
cancelable: true
});
if (!this.element.dispatchEvent(event)) return;
配置合并必须深克隆 + 默认值冻结
用户传进来的 options 是不可信的:可能缺字段、含原型链污染、甚至带 getter/setter。直接 Object.assign({}, defaults, options) 会出问题——浅拷贝无法处理嵌套对象,且会把用户传入的 undefined 覆盖默认值。
- 用
structuredClone()(现代环境)或手写简易深克隆(不含函数/循环引用即可) - 默认配置对象应通过
Object.freeze()冻结,防止被插件内部意外修改 - 对布尔/数字等基础类型配置,做显式类型校验(如
options.delay ??= 300而非options.delay || 300)
DOM 操作要预留容器控制权,不硬编码选择器
插件不该假设自己一定插在 #app 或 body 下。所有挂载点必须由用户指定,且支持多种形态:DOM 元素、CSS 选择器字符串、或返回元素的函数。
立即学习“Java免费学习笔记(深入)”;
- 不写
document.querySelector('.modal')这类固定查询,而是用options.container || document.body - 如果用户传的是字符串,内部用
querySelector()获取;如果是元素,直接使用;如果是函数,执行后取返回值 - 插入节点前检查
container.appendChild是否存在,避免跨 iframe 或 Shadow DOM 场景下报错
可扩展性真正的难点不在功能堆砌,而在约束力——你放出去的每个接口、每条配置、每次事件触发,都得经得起用户乱传、重复调用、跨框架混用的考验。漏掉一个 undefined 检查,或少封一层作用域,扩展就成了破窗。











