GitZip Pro 源码解析:一个 GitHub 文件/文件夹下载扩展是如何工作的(五)后台脚本与右键菜单
项目地址fthux/GitZipPro。本文继续分析 GitZip Pro 的 background service worker 和右键菜单实现。前两篇围绕downloader.js讲完了下载核心解析 GitHub URL、请求 API、递归收集文件、并发下载并生成 zip。生成 zip 之后下载器并没有直接完成浏览器下载而是把 base64 zip 发送给background.js。这一篇来看 GitZip Pro 的后台脚本source/background.js。background.js的职责不是操作 GitHub 页面也不是遍历文件树而是提供扩展级能力右键菜单、下载 API、系统通知、下载完成监听和历史记录保存。background 在扩展中的位置manifest.json中声明background:{service_worker:background.js,scripts:[background.js]}在 Manifest V3 中background 以 service worker 形式运行。它可以响应消息、监听事件、调用扩展 API。GitZip Pro 把必须由扩展后台执行的工作都集中到这里。整体关系可以画成这样content.js页面交互downloader.js生成 base64 zipbackground.js接收下载消息chrome.downloads.download()chrome.notificationschrome.contextMenuschrome.storage.local保存历史记录这个图里有一个双向关系content script 会把右键行信息发送给 backgroundbackground 的右键菜单点击后又会把下载命令发回 content script。菜单 ID 与后台状态background.js开头定义了右键菜单 IDconstMENU_IDS{ROOT:gitzip-pro-download,CHECKED:gitzip-pro-checked-items,SEPARATOR:gitzip-pro-separator,SELECTED:gitzip-pro-selected-item};同时还有几个后台状态constactiveDownloadsnewMap();letselectedItemHrefnull;letbackgroundTranslations{};letbackgroundLocaleen;activeDownloads用来记录当前由 GitZip Pro 触发的下载便于在下载完成时显示通知、打开下载项或保存历史记录。selectedItemHref则保存最近一次右键点击的 GitHub 文件或目录链接。后台国际化初始化background 没有页面 DOM但右键菜单和通知也需要国际化。启动时会调用asyncfunctioninitBackgroundI18n(){constlocaleawaitGZP_I18N.init();backgroundLocalelocale;backgroundTranslationsawaitGZP_I18N.loadLocale(locale);ensureContextMenus();}这里复用了i18n.js暴露的GZP_I18N。加载语言后调用ensureContextMenus()创建或更新菜单。同时background 还监听 storage 变化。当用户在 options 页面切换语言时background.js会重新加载语言包并更新菜单文案。创建和更新右键菜单右键菜单由两个函数管理ensureContextMenus()createContextMenus()ensureContextMenus()会先尝试更新已有菜单。如果更新失败说明菜单还不存在就调用createContextMenus()创建。菜单结构大致是GitZip Pro 下载已勾选项目 -------- 下载当前右键项目createContextMenus()使用chrome.contextMenus.create()创建根菜单和子菜单。菜单文案通过t(context_menu.xxx)获取。这里的设计让菜单可以随着语言切换动态更新而不是只在安装时固定一次。首次安装事件background.js监听安装事件chrome.runtime.onInstalled.addListener((details){if(details.reasoninstall){chrome.storage.local.set({gitzip-pro-show-welcome:true},(){chrome.runtime.openOptionsPage();});}});首次安装时它会写入一个 welcome 标记然后打开 options 页面。后续options.js会读取这个标记跳转到 token 页面并展示欢迎弹窗。这说明首次体验不是 popup 负责的而是 background 触发 options 页面完成的。接收下载消息下载器生成 zip 后会发送{type:GZP_DOWNLOAD_FILE,filename,base64,mimeType,notifyShow,notifyOpen,historyRecord}background.js在chrome.runtime.onMessage中处理这个消息。它先对文件名做一次安全处理safeFilenamesafeFilename.replace(/(^|\/)[.~]/g,$1_);这是因为chrome.downloads不允许路径段以点号或波浪号开头。然后把 base64 组装成 data URLconstdataUrldata:${mimeType};base64,${base64};最后调用chrome.downloads.download({url:dataUrl,filename:safeFilename,saveAs:false},callback);如果下载成功并且用户开启了通知或打开下载项background 会把下载 ID 记录到activeDownloadsactiveDownloads.set(downloadId,{notifyShow,notifyOpen,filename,historyRecord});后续下载状态变化时就能根据这个 ID 找回对应上下文。右键菜单通信当用户在 GitHub 文件行上右键时content.js会发送{type:GZP_UPDATE_CONTEXT_MENU,href,itemName,itemType}background 收到后把 href 保存到selectedItemHref并更新菜单标题。例如“下载当前选中文件 README.md”。当用户真正点击右键菜单时background 监听chrome.contextMenus.onClicked.addListener((info,tab){if(info.menuItemIdMENU_IDS.SELECTEDselectedItemHref){chrome.tabs.sendMessage(tab.id,{type:GZP_DOWNLOAD_CONTEXT_ITEM,href:selectedItemHref});}});这条消息会回到 content script。content script 再构造单项selectedItems调用GZPDownloader.start()。完整右键链路如下downloader.jsbackground.jscontent.js用户downloader.jsbackground.jscontent.js用户在文件行右键GZP_UPDATE_CONTEXT_MENU更新菜单标题并保存 href点击右键菜单GZP_DOWNLOAD_CONTEXT_ITEMstart(单个 href)下载完成监听与历史保存background 还监听浏览器下载状态chrome.downloads.onChanged.addListener((delta){...});当下载状态变成complete它会从activeDownloads中取出下载偏好如果notifyShow为 true就创建系统通知。如果notifyOpen为 true就调用chrome.downloads.show(delta.id)。如果存在historyRecord就保存下载历史。历史保存由saveHistoryRecord(record)完成。它读取STORAGE_KEYS.DOWNLOAD_HISTORY把新记录放到最前面并限制最多保留 100 条。保存完成后background 还会尝试发送chrome.runtime.sendMessage({type:GZP_DOWNLOAD_COMPLETE,record});如果 options 页面正打开它可以收到这条消息并实时刷新历史列表如果没打开也没关系因为记录已经写入 storage。本篇小结background.js是 GitZip Pro 的扩展后台层。它把下载器不能或不适合直接做的事情接了过来GZP_DOWNLOAD_FILE - chrome.downloads.download() - chrome.downloads.onChanged - notification / open downloaded item / save history同时它还负责右键菜单content.js 发送右键行信息 - background 更新菜单标题 - 用户点击菜单 - background 通知 content.js 启动下载下一篇进入options.js看 GitZip Pro 的设置页如何管理主题、语言、token、命名规则、忽略规则、下载历史和统计数据。项目源码可在 fthux/GitZipPro 查看。欢迎 Star 支持 GitZip Pro也方便后续对照源码阅读本系列。如果你也好奇这个 Pro 版为什么会做起来可以接着看这篇项目缘起用了 GitZip 这么多年我动手做了一个「Pro」版。