资讯动态

Sona源码解析二:InjectorManager如何实现Ember刷新不掉的DOM自愈注入

发布时间:2026/10/17 15:19:29 来源:尧图企业网站定制
【免费下载链接】sona基于Pengu loader的英雄联盟客户端增强插件LOL助手sona助手LOL插件A LOL plugin base on Pengu loader; sona plugin;项目地址https://gitcode.com/gh_mirrors/sona4/sona点击查看免费下载Sona 是一个基于 Pengu Loader 的英雄联盟客户端增强插件它为客户端注入入口按钮、自定义旗帜、英雄 T 级展示等大量自定义 UI。但客户端底层是 Ember.js 框架一次页面刷新就可能把注入的元素整个洗掉。本文深入解析 Sona 的 InjectorManager——用一个永不掉线的全局 MutationObserver 帧级节流实现被刷掉就自动补回的 DOM 自愈注入机制。为什么需要自愈Ember 会随时洗掉你的 DOM把自定义元素插入客户端 DOM 后下面这些情况都会让它消失玩家从主页切到召唤师峡谷等页面再切回Ember 重建了整个路由视图客户端热更新、数据刷新导致容器节点被替换某些页面卸载时连带子树一起被移除。传统做法是每个功能自己写一个轮询setTimeout反复检查结果是注入点一多几十个轮询互相打架性能差、时序乱。Sona 的解法是把所有注入点收敛到一个管理器、一个 Observer里统一调度。核心设计一个 Observer 守护所有注入任务InjectorManager 的全部状态只有三个成员作用tasks: SetInjectTask所有注册进来的注入任务集合observer: MutationObserver唯一的全局 DOM 观察者isThrottled: boolean帧级节流标志位注入任务一个尽力而为的布尔函数任务类型只有一行契约InjectorManager.ts#L16type InjectTask () boolean返回true注入成功或元素已存在本次无事可做返回false锚点元素还没出现等下一次 DOM 变动再试。这个幂等 无副作用失败的契约非常关键它让任务可以被同一帧内重复执行、任意次重复执行而不会重复插元素。例如入口按钮任务 tryInjectSonaButton 每次执行先查#sona-entry-btn是否还在不在才找 Play 按钮左侧重新插入。永不断线的 MutationObserverInjectorManager.start() 启动全局守护时做了一个刻意的反常决定从不调用disconnect()。它监听整个document.body的子树变化this.observer.observe(document.body, { childList: true, subtree: true, })于是自愈链路自然成立Ember 删掉了注入元素 → DOM 变动触发 Observer 回调 → 重跑所有任务 → 元素被补回。整个闭环不依赖任何轮询且因为只有一个 Observer开销是常数级的。requestAnimationFrame 节流一帧最多检查一次Ember 一次刷新可能触发成百上千个 mutation 事件。如果每次都重跑任务主线程会被塞爆。start() 里的节流逻辑是if (this.isThrottled) return this.isThrottled true requestAnimationFrame(() { for (const task of this.tasks) { /* 重跑全部任务 */ } this.isThrottled false })含义是一帧之内无论发生多少次 DOM 变动任务只统一执行一次。既保证了下一帧一定恢复的自愈速度又不会因为 Ember 的高频渲染而卡顿。每个任务还包在try/catch里单个任务抛错不会影响其他任务。实战走读入口按钮如何复活以 injections.ts 中的入口按钮为例完整自愈过程只有四步启动时注册registerAllInjections() 调用injector.register(tryInjectSonaButton)注册后立即执行一次首次注入找到.play-button-container的父节点把按钮insertBefore到 Play 按钮左侧Ember 刷新玩家切换页面按钮连同父节点一起被移除DOM 变动触发 Observer自动补回下一帧任务重跑按钮不存在 → 重新插入全程无需人工干预。同样的模式覆盖了插件里几乎所有注入点自定义旗帜按钮 插到身份定制页标题旁、英雄选择 T 级徽章 插到选英雄界面、在线状态菜单接管 用data-sona-hijacked属性标记已接管的节点防止重复绑定。连 Sona 自己的 React 主界面也受它保护tryGuardContainer 检查#sona-root容器是否还连着 DOM脱离就自动重新挂载——宿主界面刷新后面板失而复得。功能开关如何与注入器协作任务不是永远注册的而是跟随功能开关动态增删这对应 InjectorManager.register / unregister 两个 API开启自定义旗帜时updateCustomBanner() 调用injector.register(tryInjectCustomBannerButton)并顺手执行一次立即生效关闭时injector.unregister(...)从集合中移除任务不再被重跑同时cleanupCustomBanner()移除已注入的元素和补丁。也就是说开关关闭 任务从自愈集合中摘除Observer 会继续工作但不再为这个功能浪费检查。选英雄 T 级展示的 registerTierInjection / unregisterTierInjection 也是同样的套路进选英雄界面注册、退场时清理。与 Ember Hook 的分工DOM 层 vs 组件层Sona 的注入体系其实是双层结构机制文件作用层级解决什么InjectorManagersrc/lib/InjectorManager.tsDOM 层注入的元素被刷新掉后自动补回Ember Hooksrc/lib/ember-hook.ts组件类层通过 RCPpostInit劫持Ember.Component.extend在组件类创建时注入 Mixin 或劫持方法Ember Hook 管的是组件长什么样InjectorManager 管的是页面刷新后元素还在不在。两者互补前者负责在正确的时机改造组件后者负责在混乱的 DOM 生命周期里兜底。小结InjectorManager 用一个不到百行的单例文件回答了客户端插件最常见的难题永不掉线Observer 从不断开任何注入点被刷掉后下一次 DOM 变动自动补回绝对流畅全局唯一 Observer requestAnimationFrame节流一帧最多检查一次Ember 高频渲染也不会卡顿易于扩展新增注入点只需写一个幂等的tryInjectXxx(): boolean然后injector.register(task)一行搞定见 injections.ts 顶部注释的开发指引。对想给自己的 Pengu Loader 插件注入 UI 的开发者来说这套单一 Observer 幂等任务 帧级节流的模板几乎是照搬就能用的最佳实践。赞分享【免费下载链接】sona基于Pengu loader的英雄联盟客户端增强插件LOL助手sona助手LOL插件A LOL plugin base on Pengu loader; sona plugin;项目地址https://gitcode.com/gh_mirrors/sona4/sona点击查看免费下载相关推荐嵌入式文件系统如何实现掉电不丢数据littlefs实战解析还记得那些年我们因为突然断电而丢失的配置数据吗在嵌入式开发中数据丢失问题一直困扰着开发者。今天我们来聊聊littlefs这个专为微控制器设计的轻量级文件系统嵌入式存储系统编程LitePan 认证刷新状态机如何让多个网盘的 Token 永不掉线LitePan 认证刷新状态机如何让多个网盘的 Token 永不掉线 LitePan 是一款多网盘聚合挂载工具支持 WebDAV、STRM、媒体整理、本地挂PulltoRefresh.js源码解析深入理解下拉刷新的实现原理PulltoRefresh.js源码解析深入理解下拉刷新的实现原理 PulltoRefresh.js 是一个轻量级但功能强大的JavaScript库专门为移前端UI组件创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑