十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

Elementor Web-CLI:$e.hooks.ui 命令钩子 API 解析——在命令执行前后注入 UI 逻辑

Elementor Web-CLI:$e.hooks.ui 命令钩子 API 解析——在命令执行前后注入 UI 逻辑 Elementor Web-CLI$e.hooks.ui 命令钩子 API 解析——在命令执行前后注入 UI 逻辑【免费下载链接】elementorThe most advanced frontend drag drop page builder. Create high-end, pixel perfect websites at record speeds. Any theme, any page, any design.项目地址: https://gitcode.com/GitHub_Trending/el/elementor本文聚焦 Elementor 编辑器命令式架构Web-CLI中的$e.hooks.uiAPI。该 API 是一个专门管理 UI 钩子的注册器允许开发者在任意命令执行前before、执行后after或执行失败时catch注入自定义界面逻辑而不会污染 Elementor 的数据模型与历史History系统。读完后你将掌握 UI 钩子的四个基类$e.modules.hookUI.Base / After / Before / Catch的继承关系、完整注册与触发调用链并能直接复现两份可运行的钩子示例after 修改 DOM 类名、before 高亮新建的 Section。一、$e.hooks.ui 的定位与核心概念$e.hooks.ui是 Elementor 命令 API 中的一类钩子管理器hook manager。按照设计文档 ui.md 的描述它具备以下核心特征挂载对象钩子挂载在$e.commands上每当一个命令被运行对应事件event即被触发三个事件点before命令执行前、after命令执行后、catch命令失败时职责边界主要用于 UI/View 层面的操作例如切换视图状态、调整 DOM 类名、刷新面板显示刻意与数据模型、历史记录解耦——如果需要在数据层面介入应使用对应的 data hooks父类{HooksBase}所有通用方法register、run、getAll、get、getCallbacks等都继承自该父类。一个重要的约束所有 UI 钩子都必须通过继承$e.modules.hookUI下的类来创建而不是直接new一个裸对象。文档列出的类如下类说明$e.modules.hookUI.Base创建自定义 UI 钩子的“裸”基类不绑定任何事件。$e.modules.hookUI.After创建在命令执行之后运行的钩子。$e.modules.hookUI.Before创建在命令执行之前运行的钩子。$e.modules.hookUI.Catch创建在命令失败时运行的钩子。位置说明原文档标注的 API 位置为core/common/assets/js/api/core/hooks/ui.js在当前仓库中这套 API 实际落在 Web-CLI 模块内即 modules/web-cli/assets/js/core/hooks/ui.js模块基类位于 modules/web-cli/assets/js/modules/hooks/ui/。二、钩子类体系从$e.modules.hookUI.*到注册方法在源码中UI 钩子的四个类各自非常薄核心差异只在register()方法上它决定了钩子被注册到哪个“事件桶”里Base 只负责声明类型import HookBase from elementor-api/modules/hook-base; export class Base extends HookBase { getType() { return ui; } }After、Before、Catch 则分别把自身注册到对应事件// after.js register() { $e.hooks.registerUIAfter( this ); } // before.js register() { $e.hooks.registerUIBefore( this ); } // catch.js register() { $e.hooks.registerUICatch( this ); }这三个注册方法定义在 hooks.js 中本质是向统一注册器派发“类型 事件”二元组registerUIAfter( instance ) { return this.register( ui, after, instance ); } registerUICatch( instance ) { return this.register( ui, catch, instance ); } registerUIBefore( instance ) { return this.register( ui, before, instance ); }也就是说选择After/Before/Catch哪个基类就等价于决定了你的钩子逻辑在命令生命周期的哪个阶段执行。钩子基类 HookBase条件判断与执行入口所有钩子实例最终都来自 hook-base.js 中的HookBase。构造函数会依次调用initialize()并固化三个属性type、command要监听的全命令路径、id钩子唯一标识constructor() { this.initialize(); this.type this.getType(); this.command this.getCommand(); this.id this.getId(); }开发者通常需要覆写的方法方法是否必须作用getCommand()必须返回要挂载的命令路径如document/elements/create从源码注释看也支持命令数组。getId()必须返回钩子唯一 id重复 id 会被管理器拒绝见下文checkId。getConditions( args, result )可选返回true才会调用apply()默认恒为true。apply( args, result )必须真正的钩子逻辑。getContainerType()可选绑定容器类型用于在回调匹配阶段提前过滤源码注释说明其目的是“Used to gain performance”换取性能。执行入口run()hook-base.js#L150-L167体现了两个实用细节按 id 精确禁用命令参数中的options.callbacks可以显式关闭某个钩子例如options.callbacks: { my-hook-id: false }时run()直接返回true而不执行任何逻辑条件短路getConditions()返回 falsy 时同样跳过apply()并返回true保证命令链继续推进。run( ... args ) { const { options {} } args[ 0 ]; // Disable callback if requested by args.options. if ( options.callbacks false options.callbacks[ this.id ] ) { return true; } if ( this.getConditions( ... args ) ) { ... return this.apply( ... args ); } return true; }三、Ui 管理器before 桶与回调参数差异$e.hooks.ui管理器本身的实现在 ui.js它继承HooksBase并做了两件关键的事1. 补充 before 事件桶。父类构造器只初始化了after和catch两个桶见 base.js#L39-L52UI 管理器在构造时额外加上beforeconstructor( ... args ) { super( ... args ); this.callbacks.before {}; this.depth.before {}; }2. 按事件类型分发回调参数。不同事件点能拿到的信息不同runCallback()ui.js#L16-L32据此决定传参runCallback( event, callback, args, result ) { switch ( event ) { case before: // 命令尚未执行没有 result。 callback.callback( args ); break; case catch: case after: // 命令已执行或失败附带结果。 callback.callback( args, result ); break; default: return false; } return true; }这就是为什么示例中Before钩子的apply( args )只接收args而After钩子的apply( args, result )能拿到命令执行结果——参数差异不是约定而是管理器强制的签名。此外Ui类覆写了onRun()/onCallback()在开发环境下通过$e.devTools.log.callbacks()输出钩子运行日志方便在 DevTools 中观察钩子触发情况。注册与触发链路HooksBase 内部机制通用注册/触发逻辑位于 base.js梳理后的调用链为register( event, instance )先做三项校验——checkEvent()事件必须存在于当前类型的桶中否则抛出ui: xxx is not available.、checkInstance()实例getType()必须与管理器类型一致、checkId()id 全局唯一重复则抛出id: xxx is already in use.registerCallback( ... )把instance.run.bind( instance )包装为回调对象含id、isActive、activate()、deactivate()并按getContainerType()的结果存入callbacks[ event ][ command ][ containerType ]或callbacks[ event ][ command ].all同时写入扁平索引callbacksFlatList并记录usedIdsrun( event, command, args, result )通过getCallbacks()按“当前容器类型 all 桶”取回调集合非空时调用runCallbacks()runCallbacks()遍历回调时跳过isActive false的条目并用depth[ event ][ id ]计数器防止同一钩子递归自触发深度等于 1 时才真正执行否则静默跳过回调抛错会被捕获并通过Console.error输出唯一例外是$e.modules.HookBreak它会原样向上抛出供父级try {}处理base.js#L344-L387。查询侧的 API$e.hooks.ui.get( id )可按 id 取单个回调$e.hooks.ui.getAll()返回按事件分组的结构如getAll().after即所有 after 钩子列表activate()/deactivate()会对全部回调批量启用/停用。四、完整示例注册一个 After 钩子以下示例继承自原文档文档中注明可在控制台直接运行但依赖自定义组件示例先行注册参见 components.md 的示例 1// Example of UI hook, fired after the command runs and change ( CSS Class ) of all div elements. class CustomUIHook extends $e.modules.hookUI.After { getCommand() { // Command to listen. return custom-component/example; } getId() { // Unique id for the hook. return custom-component-example-ui-hook; } getConditions( args ) { // Conditions for the hook to be applied. if ( args.toggleClass ) { return true; } return false; } /* * The actual hook logic. */ apply( args, result ) { console.log( My hook custom logic, args: , args, result: , result ); // Add custom-component class for all div elements. document.querySelectorAll( div ).forEach( ( element ) element.classList.add( custom-component ) ); } } // Add new hook to $e.hooks.ui; const myHook new CustomUIHook(); // Output new hook. console.log( myHook ); // Output all ui hooks after. console.log( $e.hooks.ui.getAll().after ); // Test the hook. result $e.run( custom-component/example, { toggleClass: true, } ); // Output command run result. console.log( e-hooks-ui-eg-1-result:, result );结合源码看这段代码的执行要点实例化CustomUIHook时HookBase构造函数已固化type ui、command、id调用$e.run( custom-component/example, { toggleClass: true } )完成命令后管理器触发ui类型的after事件Ui.runCallback()把args与命令结果result一并传给apply()若省略toggleClass: truegetConditions()返回false钩子整体跳过但不影响命令结果——这正是“UI 钩子不影响数据模型与历史”的体现钩子只观察/装饰不改变命令返回值如需在后续运行中临时屏蔽它可在命令参数里传options: { callbacks: { custom-component-example-ui-hook: false } }。五、完整示例注册一个 Before 钩子第二个示例监听内置命令document/elements/create在容器创建之前对新建的 Section 调用视图方法toggleSectionIsFull()实现“新建 Section 即进入 full 模式”的纯 UI 增强// Example of event that toggle the section HTML class. class CreateSectionIsFull extends $e.modules.hookUI.Before { getCommand() { return document/elements/create; } getId() { return create-section-is-full; } getConditions( args ) { const { containers [ args.container ] } args; return containers.some( ( /* Container */ container ) section container.model.get( elType ) ); } apply( args ) { const { containers [ args.container ] } args; containers.forEach( ( /* Container */ container ) { if ( section container.model.get( elType ) ) { container.view.toggleSectionIsFull(); } } ); } }这个示例展示了 UI 钩子的典型用法模式getConditions()做廉价预判通过容器模型elType判断本次创建是否涉及 Section只有相关时才进入apply()避免无谓开销apply( args )只接收 args因为命令尚未执行没有result可传这与第二节的runCallback()分发逻辑一致直接操作container.view钩子拿到的args.containers是编辑器容器对象可以在其视图层执行toggleSectionIsFull()这类展示逻辑而不需要改动命令本身的数据行为。六、内置 UI 钩子参考与使用建议内置实现参考编辑器自带的 UI 钩子集中在 assets/dev/js/editor/document/hooks/ui按文档/容器操作类型划分了create/、delete/、document/、settings/等子目录并在index.js中统一导出。阅读这些文件是学习“标准写法”最直接的途径——它们示范了如何用getConditions()精确限定触发范围、如何只触碰视图层状态选择事件点的决策依据需要拿到命令执行结果如新创建元素 id 的视图刷新用After需要在命令产生数据变更前抢先做 UI 准备如高亮、展开面板用Before需要展示命令失败后的 UI 反馈用Catch命名与 id 规范getId()必须全局唯一注册时复用 id 会直接抛错建议沿用功能-场景的命名风格如create-section-is-full边界提醒UI 钩子不能修改数据模型与历史。若你的目标是在命令执行中改写参数或数据应改用 data 钩子$e.modules.hookData.*见 data.mdUI 钩子的apply()返回值也不会替换命令结果它只是命令生命周期上的旁路观察者。相关文档钩子系统总览hooks.md命令 API$e.commandscommands.md组件 API示例依赖components.md【免费下载链接】elementorThe most advanced frontend drag drop page builder. Create high-end, pixel perfect websites at record speeds. Any theme, any page, any design.项目地址: https://gitcode.com/GitHub_Trending/el/elementor创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表