ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Handsontable 自定义插件开发指南:基于 BasePlugin 扩展数据网格功能

Handsontable 自定义插件开发指南:基于 BasePlugin 扩展数据网格功能 前端UI组件【免费下载链接】handsontableJavaScript Data Grid / Data Table with a Spreadsheet Look Feel. Works with React, Angular, and Vue. Supported by the Handsontable team ⚡项目地址https://gitcode.com/gh_mirrors/ha/handsontable点击查看免费下载本文档对应的原始指南为 docs/content/guides/tools-and-building/custom-plugins/custom-plugins.md正文中的代码示例与步骤与该指南保持一致并辅以当前仓库源码佐证。Handsontable 的大部分内置能力排序、筛选、合并单元格、隐藏行列等本身都是插件本文带你从零编写一个自定义插件继承BasePlugin、通过registerPlugin注册、在初始化配置中开启并接入插件生命周期enable / disable / update / destroy。读完你既能写出可复用的自定义插件也能理解 Handsontable 插件系统的底层工作机制。什么时候该写插件而不是加一个 Hook插件是扩展 Handsontable 能力的推荐方式内置功能几乎全部由插件提供见 handsontable/src/plugins/index.ts 中的registerAllPlugins()列表。当出现以下需求时适合编写自定义插件需要在多个 Hook 之间共享状态需要enable / disable / update 的生命周期管理需要提供可注册、可复用、可交付给其他开发者的独立单元。如果只是对单个动作做响应直接用 Hook 即可相关说明见 Events and hooks 指南。在 React、Angular、Vue 项目中你依然用 JavaScript 编写插件类再从框架组件中引用它——插件本身与框架无关。插件系统的工作原理写插件前先理解它如何被 Handsontable 加载注册通过registerPlugin(PluginClass)把插件类登记进全局插件注册表handsontable/src/plugins/registry.ts插件类上的静态PLUGIN_KEY会被提取为注册名实例化在 Handsontable 实例构造时core.ts遍历所有已注册插件为每个插件创建实例并存入当前实例的pluginsRegistry见 handsontable/src/core.ts启用所有插件初始化完成后触发afterPluginsInitializedHookBasePlugin会检查插件的isEnabled()为true时调用enablePlugin()见 handsontable/src/plugins/base/base.ts使用通过hot.getPlugin(pluginKey)拿到插件实例调用其公开方法见 handsontable/src/core.tsgetPlugin内部会对插件名做首字母大写归一化后查询。第 1 步前置准备导入两样东西BasePlugin—— 内置的插件基类接口让你融入 Handsontable 生命周期registerPlugin—— 把插件注册进全局插件注册表的工具函数。import { BasePlugin, registerPlugin } from handsontable/plugins;registerPlugin支持三种参数形态见 registry.tsregisterPlugin(PluginClass)从类的静态PLUGIN_KEY取注册名registerPlugin(CustomAlias, PluginClass)显式指定注册名别名registerPlugin(PluginClass, undefined, priority)指定优先级通过静态PLUGIN_PRIORITY或第三参数注册表会先按优先级升序、再按注册顺序输出插件队列见getPluginsNames()。注册名会被自动转换为首字母大写形式且不允许重复注册——重复注册会抛出There is already registered X plugin.错误。第 2 步扩展 BasePlugin扩展BasePlugin是编写自定义插件的最佳起点。基类替你处理了向后兼容性基类的公共接口保持稳定内存泄漏防护disablePlugin()与destroy()会自动清理事件与 Hook 连接clearHooks()、eventManager.clear()实例正确绑定构造器通过defineGetter(this, hot, hotInstance, { writable: false })注入只读的this.hot引用见 base.ts它指向当前 Handsontable 实例无法被覆盖并可用它访问行列索引映射器index mappers。下面是一份完整可运行的自定义插件骨架逐段说明每个成员的作用export class CustomPlugin extends BasePlugin { /** * 定义插件的唯一键字符串即插件别名。 * Handsontable 以该别名注册插件 * 当 updateSettings() 调用中包含该别名时插件状态会被更新 * 初始化时也可用该别名识别传给 Settings 对象的插件选项。 * * returns {string} */ static get PLUGIN_KEY() { return customPlugin; } /** * 定义额外的设置键字符串数组。 * 当 updateSettings() 调用中包含至少一个这些设置键时插件状态会被更新。 * 若返回 true插件在每次 updateSettings() 时都更新 * 若返回 false插件在任意 updateSettings() 时都不更新。 * * returns {Array|boolean} */ static get SETTING_KEYS() { return true; } /** * 扩展默认构造函数定义插件内部属性。 * * param {Handsontable} hotInstance */ constructor(hotInstance) { super(hotInstance); // 在构造函数中初始化所有公共属性 this.configuration { enabled: false, msg: }; } /** * 统一解析传入设置对象的插件配置。 * * returns {object} * throws {Error} */ getUnifiedConfig() { const pluginSettings this.hot.getSettings()[CustomPlugin.PLUGIN_KEY]; if (pluginSettings true) { return { enabled: true, msg: default msg boolean }; } if (Object.prototype.toString.call(pluginSettings) [object Object]) { return { enabled: true, msg: default msg obj, ...pluginSettings }; } if (pluginSettings false) { return { enabled: false, msg: }; } throw new Error( ${CustomPlugin.PLUGIN_KEY} - incorrect plugins configuration. Passed: - type: ${typeof pluginSettings} - value: ${JSON.stringify(pluginSettings, null, )} Expected: - boolean - object ); } /** * 检查插件是否在设置中被启用。 */ isEnabled() { const pluginSettings this.getUnifiedConfig(); return pluginSettings.enabled; } /** * enablePlugin 在 beforeInit 钩子上触发实际由基类在 afterPluginsInitialized 时调用。 * 应包含插件的初始设置与 Hook 连接。 * 仅当 isEnabled 返回 true 时该方法才会被运行。 */ enablePlugin() { // 从初始化对象中获取插件配置 this.configuration this.getUnifiedConfig(); // 在此添加所有插件 Hook。建议使用箭头函数保持 this 指向插件实例。 this.addHook(afterChange, (changes, source) this.onAfterChange(changes, source)); // super 方法将 this.enabled 置为 true // 这是正确更新插件设置的必需步骤。 super.enablePlugin(); } /** * disablePlugin 禁用插件。 */ disablePlugin() { // 在此将所有插件类属性重置为默认值 this.configuration null; // BasePlugin.disablePlugin 负责清理 Hook 连接 // 并将 false 赋给 this.enabled 属性。 super.disablePlugin(); } /** * updatePlugin 在 afterUpdateSettings 钩子上被调用 * 除非 updateSettings 把插件关掉了 * 且仅当传给 updateSettings 的配置对象包含与该插件相关的条目时。 * * updatePlugin 应包含插件在实例设置更新后正常工作的全部逻辑。 */ updatePlugin() { // updatePlugin 需要包含正确重新启用插件所需的全部代码。 // 大多数情况下简单地禁用再启用即可。 const { enabled, msg } this.getUnifiedConfig(); // 你可以决定更新设置是否触发 disable-enable 例程。 if (enabled false this.enabled true) { this.disablePlugin(); } else if (enabled true this.enabled false) { this.enablePlugin(); } // 如果只需要更新单个选项 if (this.configuration ! null msg this.configuration.msg ! msg) { this.configuration.msg msg; } super.updatePlugin(); } /** * 定义你的外部方法。 */ externalMethodExample() { // 方法定义 } /** * afterChange 钩子回调。 * * param {CellChange[]} changes 变更数组。 * param {string} source 描述变更来源。 */ onAfterChange(changes, source) { // afterChange 回调逻辑 console.log( ${CustomPlugin.PLUGIN_KEY}.onAfterChange - ${this.configuration.msg}, changes, source ); } /** * destroy 是清理插件生命周期中创建的实例、 * 对象或索引映射器的最佳位置。 */ destroy() { // super 方法清理插件的事件回调、Hook 连接与属性。 super.destroy(); } }逐成员要点说明PLUGIN_KEY静态 getter插件的唯一标识既是注册名也是初始化设置对象里的键名。基类默认返回base见 base.ts自定义插件必须覆盖它。SETTING_KEYS静态 getter控制updateSettings()时插件是否参与更新。基类默认返回[this.PLUGIN_KEY]。基类onUpdateSettings()中的#isRelevantToSettings()会这样判断见 base.ts返回true无论updateSettings传什么内容都更新插件返回false任何updateSettings都不更新插件返回字符串数组只要传入的设置对象里出现其中任一键就更新插件。构造函数中的this.hot基类构造器以只读方式注入可直接读取this.hot.getSettings()获取当前实例的完整设置。getUnifiedConfig()把用户传入的三种配置形态true、false、对象统一归一为内部配置对象。传入非法类型如字符串时抛出带类型与值的明确错误方便排查配置问题。这是 Handsontable 内置插件的常见做法——配置可以是布尔开关也可以是带选项的对象。enablePlugin()插件的初始化入口。先读取统一配置再用this.addHook(...)挂载业务 HookaddHook是基类提供的受管 API会在disablePlugin()/destroy()时自动解绑见 base.ts最后必须调用super.enablePlugin()把this.enabled置为true。disablePlugin()重置插件内部属性调用super.disablePlugin()—— 基类会清空eventManager与全部已注册 Hook并把this.enabled置为false。updatePlugin()设置在运行时变化时的响应入口。基类onUpdateSettings()见 base.ts会先判断插件与本次设置是否相关、是否发生 enable/disable 翻转然后调用updatePluginSettings()与updatePlugin()。文档建议采用“禁用再启用”的简单可靠策略也可按需只更新单个配置项。destroy()释放插件持有的实例、对象或索引映射器。基类destroy()会清空设置、销毁EventManager、移除全部 Hook并把除hot外的所有属性置空见 base.ts。第 3 步注册 CustomPlugin注册插件有两条路径方式一借助静态PLUGIN_KEY——registerPlugin会把类的PLUGIN_KEY用作注册别名即上面骨架里的用法// 必须先注册插件才能在你的 Handsontable 实例中使用它。 registerPlugin(CustomPlugin);方式二使用自定义别名—— 把字符串放在第一个参数注册器会用该字符串作为别名而不再读取CustomPlugin的PLUGIN_KEYgetterregisterPlugin(CustomAlias, CustomPlugin);两种方式最终都进入_registerPlugin()别名统一首字母大写后存入注册表重复注册会抛错见 registry.ts。需要注意的是registerPlugin在浏览器/打包环境通常只需调用一次模块级副作用随后所有新建的 Handsontable 实例都能识别该插件。第 4 步在 Handsontable 中使用插件通过初始化设置对象里的customPlugin键即PLUGIN_KEY的值控制插件传true以默认配置启用、传对象携带选项、传false保持禁用。JavaScriptimport Handsontable from handsontable; import { CustomPlugin } from ./customPlugin; const hotInstance new Handsontable(container, { // 传 true 以默认选项启用插件 [CustomPlugin.PLUGIN_KEY]: true, // 也可以传带选项的对象来启用插件 [CustomPlugin.PLUGIN_KEY]: { msg: user-defined message, }, // 也可以一开始不启用插件 [CustomPlugin.PLUGIN_KEY]: false, });React配合HotTableimport Handsontable from handsontable; import { CustomPlugin } from ./customPlugin; HotTable // 传 true 以默认选项启用插件 customPlugin{true} // 也可以传带选项的对象来启用插件 customPlugin{{ msg: user-defined message, }} // 也可以一开始不启用插件 customPlugin{false} /Angular绑定到HotTable组件的settingssettings { // 传 true 以默认选项启用插件 customPlugin: true, // 也可以传带选项的对象来启用插件 customPlugin: { msg: user-defined message, }, // 也可以一开始不启用插件 customPlugin: false, };hot-table [settings]settings/hot-tableVue 3通过ref管理的GridSettingsscript setup langts import { ref } from vue; import { HotTable } from handsontable/vue3; import type { GridSettings } from handsontable/settings; const settings refGridSettings({ // 传 true 以默认选项启用插件 customPlugin: true, // 也可以传带选项的对象来启用插件 customPlugin: { msg: user-defined message, }, // 也可以一开始不启用插件 customPlugin: false, }); /script template HotTable :settingssettings / /template注意上面的 JavaScript 示例中三个键同名实际使用时只需按需传其中一个这里并列展示三种写法仅为说明可选项。运行时更新调用hot.updateSettings({ customPlugin: { msg: new message } })时基类的onUpdateSettings会检测到相关键更新插件配置并触发updatePlugin()从而走 disable→enable 或单选项更新逻辑。第 5 步获取插件实例引用要调用插件对外暴露的 API如externalMethodExample()通过核心方法getPlugin()拿到实例。JavaScriptconst pluginInstance hotInstance.getPlugin(CustomPlugin.PLUGIN_KEY); pluginInstance.externalMethodExample();React使用useRef创建HotTable组件引用读取其hotInstance属性const hotTableComponentRef useRef(null); const pluginInstance hotTableComponentRef.current.hotInstance.getPlugin(CustomPlugin.PLUGIN_KEY);Angular使用ViewChild拿到HotTableComponent在ngAfterViewInit中读取hotInstanceimport { Component, ViewChild, AfterViewInit } from angular/core; import { GridSettings, HotTableComponent, HotTableModule, } from handsontable/angular-wrapper; Component({ standalone: true, imports: [HotTableModule], template: div hot-table [settings]gridSettings / /div, }) export class ExampleComponent implements AfterViewInit { ViewChild(HotTableComponent, { static: false }) readonly hotTable!: HotTableComponent; readonly gridSettings GridSettings{ columns: [{}], }; ngAfterViewInit(): void { this.hotTable?.hotInstance?.getPlugin(CustomPlugin.PLUGIN_KEY); } }Vue 3通过模板引用读取hotInstancescript setup langts import { ref } from vue; import { HotTable } from handsontable/vue3; import { CustomPlugin } from ./customPlugin; const hotRef refInstanceTypetypeof HotTable | null(null); function usePlugin() { const pluginInstance hotRef.value?.hotInstance?.getPlugin(CustomPlugin.PLUGIN_KEY); pluginInstance?.externalMethodExample(); } /script template HotTable refhotRef :settingssettings / /templategetPlugin在 core.ts 中的实现会先对插件名做首字母大写归一化再从当前实例的pluginsRegistry中查找并返回插件实例未找到时返回undefined因此调用外部方法前建议做空值判断。插件生命周期速查阶段触发时机插件内对应方法构造实例创建时所有插件类被new出来constructor(hotInstance)初始化实例的beforeInitinit()基类内部解析插件名、应用设置、校验依赖启用所有插件初始化完成afterPluginsInitialized且isEnabled()为真enablePlugin()更新updateSettings()且设置与SETTING_KEYS相关updatePlugin()→ 内部可触发 disable/enable禁用updateSettings()把配置置为false或与硬冲突设置冲突disablePlugin()销毁实例销毁时destroy()基类还提供了两个可选静态成员供进阶插件使用DEFAULT_SETTINGS插件默认设置对象base.tsgetSetting()会把默认值与当前设置合并SETTINGS_VALIDATORS设置校验器返回false时对应选项会被忽略并给出警告base.tsPLUGIN_DEPS插件依赖声明格式type:ModuleName如plugin:filters缺失时基类会收集并抛出缺少依赖的错误base.ts。相关 API 参考基类BasePlugin—— 本文所有生命周期方法enablePlugin/disablePlugin/updatePlugin/addHook/destroy的底层实现注册表registerPlugin/getPlugin/hasPlugin/getPluginsNames—— 插件注册与查询工具核心方法getPlugin()—— 按名称获取插件实例核心方法updateSettings()—— 运行时更新设置并触发插件更新流程HookafterPluginsInitialized—— 所有插件初始化完成后触发BasePlugin.init()依赖它来决定是否调用enablePlugin()见 base.ts内置插件清单handsontable/src/plugins/index.ts —— 参考 40 余个内置插件的注册方式与PluginClassMap类型声明模式可当作自定义插件的范本。赞分享前端UI组件【免费下载链接】handsontableJavaScript Data Grid / Data Table with a Spreadsheet Look Feel. Works with React, Angular, and Vue. Supported by the Handsontable team ⚡项目地址https://gitcode.com/gh_mirrors/ha/handsontable点击查看免费下载相关推荐Handsontable插件开发与自定义扩展Handsontable插件开发与自定义扩展 本文全面介绍了Handsontable插件系统的开发与自定义扩展能力涵盖了插件架构基础、自定义插件创建、生命周期前端UI组件革命性文本生成模型free-solar-evo-v0.11-openmind开启NPU加速的AI对话新纪元革命性文本生成模型free solar evo v0.11 openmind开启NPU加速的AI对话新纪元 free solar evo v0.11 openTanStack TableAlpine自定义功能开发指南基于 tableFeatures 的插件化扩展TanStack TableAlpine自定义功能开发指南基于 tableFeatures 的插件化扩展 本篇指南以 TanStack Table 的 A前端UI组件上一篇开源协议转换神器DavMail技术选型、社区支持与未来 roadmap下一篇HumanLayer Skills 深度解析workflow-template.yml 每一行到底在做什么创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表