
Electron PreloadScript 对象深度解析session 级预加载脚本的注册、执行与底层实现【免费下载链接】electron:electron: Build cross-platform desktop apps with JavaScript, HTML, and CSS项目地址: https://gitcode.com/GitHub_Trending/el/electronPreloadScript是 ElectronSessionAPI 中表示已注册预加载脚本的数据结构由ses.getPreloadScripts()返回用于描述某条预加载脚本的执行上下文frame或service-worker、唯一标识与脚本文件路径。本文围绕这一对象展开讲清它与PreloadScriptRegistration的区别、如何配合registerPreloadScript/unregisterPreloadScript使用并结合 Electron 源码还原其在 C 层的存储与校验逻辑帮你彻底掌握 session 级预加载脚本的完整生命周期。PreloadScript 对象的三个字段PreloadScript对象的结构定义见 preload-script.md包含且仅包含以下三个字段typestring — 预加载脚本将在其中执行的上下文类型。可能的取值为frame或service-workerframe脚本注入到该 session 关联的所有 WebContents 页面帧中在 WebContents 自身webPreferences.preload定义的脚本之前执行service-worker脚本注入到该 session 关联的 Service Worker 中。idstring — 预加载脚本的唯一 ID。注册时若未显式指定Electron 会生成一个随机 UUID这一点由其注册参数结构 PreloadScriptRegistration 中id(optional) - Unique ID of preload script. Defaults to a random UUID的说明可以确认。id是后续调用unregisterPreloadScript(id)解除注册的唯一句柄。filePathstring — 脚本文件的路径必须是绝对路径这一点在 C 实现中有硬性校验见下文源码中的路径校验一节。理解这个对象的关键在于区分两个方向输入方向是PreloadScriptRegistration对象见 preload-script-registration.md你把它传给ses.registerPreloadScript(script)来注册脚本此时id字段是可选的输出方向是PreloadScript对象ses.getPreloadScripts()返回的就是PreloadScript[]数组此时id一定是已确定的具体值显式指定或随机生成的 UUID。两者字段几乎相同核心差异就是id的可选项性——这正是注册参数与注册结果两类结构在 API 设计上的典型区分。PreloadScript 的获取来源ses.getPreloadScripts()PreloadScript对象不是手动构造的它的唯一产生途径是Session的查询 API。Session提供了一组围绕预加载脚本管理的方法见 session.mdses.registerPreloadScript(script)scriptPreloadScriptRegistration - Preload scriptRegisters preload script that will be executed in its associated context type in this session. Forframecontexts, this will run prior to any preload defined in the web preferences of a WebContents.Returnsstring- The ID of the registered preload script.即注册方法返回字符串形式的 ID——这个 ID 随后会出现在getPreloadScripts()返回的PreloadScript.id中。ses.unregisterPreloadScript(id)idstring - Preload script IDUnregisters script. 通过PreloadScript.id或注册时的返回值定位并移除脚本。ses.getPreloadScripts()ReturnsPreloadScript[]: An array of paths to preload scripts that have been registered.三个方法构成完整的增、删、查闭环registerPreloadScript创建条目并返回idgetPreloadScripts以PreloadScript数组形式暴露当前注册表unregisterPreloadScript按id删除条目。const { session } require(electron); const path require(path); const ses session.fromPartition(persist:main); // 注册 frame 上下文的预加载脚本 const id ses.registerPreloadScript({ type: frame, id: app-preload, filePath: path.join(__dirname, preload.js) }); // 查询当前已注册的全部 PreloadScript const scripts ses.getPreloadScripts(); console.log(scripts); // [ // { // type: frame, // id: app-preload, // filePath: /abs/path/to/preload.js // } // ] // 按 id 解除注册 ses.unregisterPreloadScript(id);与已废弃 API 的关系PreloadScript 如何统一了旧行为PreloadScript结构是新一代 preload 注册机制的一部分取代了更早的ses.setPreloads(preloads)与ses.getPreloads()。官方迁移说明见 breaking-changes.mdregisterPreloadScript,unregisterPreloadScript, andgetPreloadScriptsare introduced as a replacement for the deprecated methods. These new APIs allow third-party libraries to register preload scripts without replacing existing scripts. Also, the newtypeoption allows for additional preload targets beyondframe.// Deprecated session.setPreloads([path.join(__dirname, preload.js)]) // Replace with: session.registerPreloadScript({ type: frame, id: app-preload, filePath: path.join(__dirname, preload.js) })这段迁移带来了两个重要语义变化从整体覆盖变为增量注册。旧的setPreloads会清空并替换整个脚本列表新 API 按 ID 逐条增删多个第三方库可以各自注册自己的 preload 而互不覆盖。引入了type维度。PreloadScript.type字段使脚本可以针对frame或service-worker两类执行上下文分别注册这是旧 API 不具备的能力。仓库中对旧 API 的兼容实现位于 session.ts其中setPreloads的 JS 层实现清晰地展示了新旧 API 的映射关系const setPreloadsDeprecated deprecate.warnOnce(session.setPreloads, session.registerPreloadScript); Session.prototype.setPreloads function (preloads) { setPreloadsDeprecated(); this.getPreloadScripts() .filter((script) script.type frame) .forEach((script) { this.unregisterPreloadScript(script.id); }); preloads .map( (filePath) ({ type: frame, filePath, _deprecated: true }) as Electron.PreloadScriptRegistration ) .forEach((script) { this.registerPreloadScript(script); }); };可以看到废弃的setPreloads内部正是先遍历getPreloadScripts()拿到PreloadScript数组、过滤出type frame的条目并逐一unregisterPreloadScript(script.id)再把路径数组逐条包装为{ type: frame, filePath }调用新的registerPreloadScript——即旧 API 的覆盖语义是在新 API 的增删语义之上模拟出来的。同样getPreloads也只是对getPreloadScripts()做type frame过滤后映射出filePath数组与 session.md 中This will only return preload script paths forframecontext types的说明完全一致。源码中的存储模型与校验逻辑Electron 的 C 实现位于 electron_api_session.cc其中Session::RegisterPreloadScript约 L1110 起揭示了PreloadScript注册表的具体行为std::string Session::RegisterPreloadScript( gin_helper::ErrorThrower thrower, const PreloadScript new_preload_script) { auto* prefs SessionPreferences::FromBrowserContext(browser_context()); DCHECK(prefs); auto preload_scripts prefs-preload_scripts(); auto it std::find_if(preload_scripts.begin(), preload_scripts.end(), new_preload_script { return script.id new_preload_script.id; }); if (it ! preload_scripts.end()) { thrower.ThrowError( absl::StrFormat(Cannot register preload script with existing ID %s, new_preload_script.id)); return ; } if (!new_preload_script.file_path.IsAbsolute()) { // Deprecated preload scripts logged error without throwing. if (new_preload_script.deprecated) { LOG(ERROR) preload script must have absolute path: ... } else { thrower.ThrowError( absl::StrFormat(Preload script must have absolute path: %s, ...)); return ; } } preload_scripts.push_back(new_preload_script); return new_preload_script.id; }从这段源码可以确认PreloadScript的三个关键行为事实存储位置所有已注册的PreloadScript以数组形式保存在该 session 对应BrowserContext的SessionPreferencesprefs-preload_scripts()中GetPreloadScripts()L1174直接返回该数组——这解释了为什么getPreloadScripts()是同步方法。ID 唯一性约束注册时会在数组中按id查找若已存在同 ID 条目则抛出Cannot register preload script with existing ID id错误。因此当不指定id依赖随机 UUID 时几乎不会冲突而显式指定id时必须保证全局唯一。绝对路径的强制校验filePath若不是绝对路径新 API 会抛出Preload script must have absolute path: ...错误。源码同时保留了一个deprecated标志的特殊处理——来自旧setPreloads的条目即上文 TS 层注入的_deprecated: true只记录LOG(ERROR)而不抛错以维持向后兼容。这与 session.ts 中包装对象携带_deprecated字段的细节相互印证。此外RegisterPreloadScript末尾的注释说明了一个service-worker类型的执行时机细节A service worker that starts after this point picks up the new preload automatically —GetServiceWorkerStartupData()rebuilds fromSessionPreferenceson every StartWorker.即 Service Worker 的启动数据在每次启动时都从SessionPreferences重新构建因此在 Service Worker 启动之后注册的脚本无需手动重启 SW 即可生效。而UnregisterPreloadScriptL1150 起则对 ID 做了对称处理按id在数组中查找并erase找不到时抛出Cannot unregister preload script with non-existing ID id错误——因此id一旦丢失就无法再解注册该脚本建议妥善保存注册返回值。这三个方法通过.SetMethod(registerPreloadScript, ...)/.SetMethod(unregisterPreloadScript, ...)/.SetMethod(getPreloadScripts, ...)electron_api_session.cc绑定到 JS 层的Session原型上。type字段的两种取值与执行位置PreloadScript.type决定了脚本注入到哪个执行上下文type: frame脚本在该 session 关联的所有 WebContents 的页面帧中执行且按照 session.md 的说明它先于 WebContents 自身webPreferences.preload中定义的 preload 脚本运行。这一执行顺序让 session 级 preload 成为全局前置注入点适合做跨页面的统一埋点、策略拦截或环境补丁。type: service-worker脚本注入到该 session 的 Service Worker 执行环境中。session.md 文档明确registerPreloadScript会executed in its associated context type in this session而旧getPreloads的弃用说明This will only return preload script paths forframecontext types也侧面印证新机制下确实存在非frame的执行目标。仓库测试 api-service-worker-main-spec.ts 中使用了PreloadScriptRegistration相关 API可作为 Service Worker preload 行为可用性的回归验证参考。由于一个 session 可同时持有多个PreloadScript条目frame 与 service-worker 并存、同类型多条并存基于 ID 的增删查模型使得这类组合管理成为可能这也是旧setPreloads数组覆盖式 API 无法表达的能力。使用建议与小结基于以上文档与源码证据使用PreloadScript机制时可以遵循以下实践优先使用新 APIregisterPreloadScript/unregisterPreloadScript/getPreloadScripts。setPreloads/getPreloads已废弃且仅覆盖frame类型显式指定语义化的id便于日志排查与后续精确解注册但注意 ID 重复注册会直接抛错同一应用生命周期内需保证唯一filePath一律传绝对路径新 API 下相对路径会抛错Preload script must have absolute path可用path.join(__dirname, preload.js)之类的方式保证绝对性利用type区分注入目标页面级逻辑用frameService Worker 级逻辑用service-worker二者互不干扰妥善保存注册返回值id是解注册的唯一依据丢失后只能遍历getPreloadScripts()按filePath反查。PreloadScript对象虽然只有三个字段但它是 Electron 预加载脚本管理从整体覆盖走向按 ID 增量管理、按上下文类型分发这一演进的核心载体。通过session.md的 API 文档、breaking-changes.md 的迁移指引、session.ts 的兼容层实现以及 electron_api_session.cc 的 C 校验逻辑可以完整还原这一对象从注册、存储、查询到解注册的全部行为也为在自研应用中安全地管理 session 级 preload 提供了明确的实现依据。【免费下载链接】electron:electron: Build cross-platform desktop apps with JavaScript, HTML, and CSS项目地址: https://gitcode.com/GitHub_Trending/el/electron创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考