chrome.alarms API 实战指南:基于 chrome-extensions-samples 构建可交互的闹钟管理扩展
2026/9/21 7:39:38 网站建设 项目流程
  • 示例工程

【免费下载链接】chrome-extensions-samples

Chrome Extensions Samples

项目地址:https://gitcode.com/gh_mirrors/ch/chrome-extensions-samples
点击查看免费下载

导读

本文基于 chrome-extensions-samples 仓库中的 api-samples/alarms 示例,系统讲解 Manifest V3 下chrome.alarmsAPI 的完整用法。示例通过扩展页面让用户自由创建、查看、取消闹钟,并实时记录触发日志,覆盖了createclearclearAllgetAllonAlarm监听器的全部核心场景。读完本文,你将掌握定时任务扩展从「初始化默认闹钟」到「用户交互创建闹钟」的完整实现路径,并能直接复用仓库中的代码搭建自己的闹钟应用。

示例概览:一个完整的闹钟管理 Demo

api-samples/alarms是一个轻量但功能完整的演示扩展:安装扩展后,后台 Service Worker 会自动创建一个初始闹钟,同时打开 index.html 演示页面;用户可以在页面上填写表单创建自定义闹钟,实时查看"当前闹钟列表"与"闹钟事件日志",并逐个或全部取消闹钟。

从仓库结构看,该示例由以下文件组成:

文件职责
manifest.jsonMV3 清单,声明alarms权限与后台 Service Worker
bg-wrapper.jsService Worker 入口,通过importScripts加载业务逻辑
background.js安装初始化、创建默认闹钟、打开演示页
index.html演示页面结构:创建表单、闹钟列表、日志区
index.css页面样式
index.js页面逻辑:AlarmManager类封装全部闹钟操作
README.md官方说明文档

运行扩展的四个步骤

按照 README.md 的指引,运行该示例只需四步:

  1. 克隆仓库,获取chrome-extensions-samples的完整代码。
  2. 以"加载已解压的扩展程序"方式加载api-samples/alarms目录:打开chrome://extensions,开启"开发者模式",点击"加载已解压的扩展程序",选择该目录即可。
  3. 将扩展固定到工具栏,以便访问其操作按钮(action button)。
  4. 点击操作按钮打开扩展弹窗/页面并与 UI 交互:本示例中点击 action 会通过chrome.tabs.create打开index.html演示页,而不是弹出一个 popup。

注意:本示例的manifest.jsonaction字段为空对象{},未声明默认弹窗(default_popup),因此点击按钮触发的是 background.js 中注册的chrome.action.onClicked监听器,逻辑是打开演示标签页。

清单配置:声明 alarms 权限与后台脚本

manifest.json 是 MV3 扩展的入口配置,内容如下:

{ "name": "Alarms API Demo", "version": "1.0", "description": "Uses the chrome.alarms API to allow the user to set alarms using an extension page.", "manifest_version": 3, "background": { "service_worker": "bg-wrapper.js" }, "permissions": ["alarms"], "action": {} }

关键点有三:

  • "permissions": ["alarms"]:使用chrome.alarmsAPI 必须在清单中显式声明alarms权限,否则 API 不可用。
  • "background": { "service_worker": "bg-wrapper.js" }:MV3 不再支持持久化的背景页,取而代之的是事件驱动的 Service Worker。本示例采用"wrapper + 业务文件"的双文件结构,让业务逻辑可以像传统脚本一样被加载。
  • "action": {}:声明操作按钮但未配置默认弹窗,配合chrome.action.onClicked实现"点击打开演示页"的行为。

bg-wrapper.js的全部内容只是一个容错加载器:

try { importScripts('background.js'); } catch (error) { console.error(error); }

importScripts包裹在try/catch中,即使业务脚本抛错,Service Worker 也能继续存活,便于在扩展调试页面排查问题。

安装初始化:自动创建默认闹钟

background.js 承担了安装初始化职责。扩展首次安装时,会打开演示页并创建一个"占位"闹钟,保证用户进入演示页时"有东西可看":

// Initialize the demo on install chrome.runtime.onInstalled.addListener(({ reason }) => { if (reason !== chrome.runtime.OnInstalledReason.INSTALL) { return; } openDemoTab(); // Create an alarm so we have something to look at in the demo chrome.alarms.create('demo-default-alarm', { delayInMinutes: 1, periodInMinutes: 1 }); }); chrome.action.onClicked.addListener(openDemoTab); function openDemoTab() { chrome.tabs.create({ url: 'index.html' }); }

代码中有几个值得学习的工程细节:

  • 区分安装原因onInstalledreason只有等于INSTALL(首次安装)时才执行初始化逻辑,更新(UPDATE)或浏览器更新(BROWSER_UPDATE)时不重复创建闹钟,避免重复副作用。
  • chrome.alarms.create(name, alarmInfo)的两种入参形式create的第一个参数是可选的闹钟名称,省略时使用默认名''。第二个参数alarmInfo必须提供whendelayInMinutes之一(详见下文)。
  • 周期闹钟:同时指定delayInMinutesperiodInMinutes,闹钟会在 1 分钟后首次触发,之后每 1 分钟触发一次。
  • 打开页面复用同一函数openDemoTab同时被安装监听器和 action 点击监听器调用,保证"安装后"与"手动点击"的体验一致。

表单交互:理解 when 与 delayInMinutes 两种计时方式

演示页 index.html 中的创建表单包含三个输入:闹钟名称(alarm-name)、初始延迟(time-value+time-format)、重复周期(period,单位分钟)。其中延迟时间支持两种单位——分钟(minutes,默认)与毫秒(milliseconds),这正好对应chrome.alarms.create的两种计时参数。

页面脚本 index.js 在表单提交时解析用户输入并组装alarmInfo

form.addEventListener('submit', (event) => { event.preventDefault(); const formData = new FormData(form); const data = Object.fromEntries(formData); const name = data['alarm-name']; const delay = Number.parseFloat(data['time-value']); const delayFormat = data['time-format']; const period = Number.parseFloat(data['period']); const alarmInfo = {}; if (delayFormat === 'ms') { // Specified in milliseconds, use `when` property alarmInfo.when = Date.now() + delay; } else if (delayFormat === 'min') { // specified in minutes, use `delayInMinutes` property alarmInfo.delayInMinutes = delay; } if (period) { alarmInfo.periodInMinutes = period; } // Create the alarm – this uses the same signature as chrome.alarms.create manager.createAlarm(name, alarmInfo); });

这段代码完整展示了alarmInfo的组装逻辑,其背后对应chrome.alarms.create的参数约定:

alarmInfo 字段作用说明
when绝对触发时间毫秒级时间戳,例如Date.now() + 5000表示 5 秒后触发;whendelayInMinutes二者必须提供其一,若同时提供,when优先
delayInMinutes相对延迟指定后闹钟在约delayInMinutes分钟后触发(打包成 CRX 分发时最小值限制为 1 分钟,详见下文)
periodInMinutes重复周期若提供,闹钟会按此周期循环触发;与whendelayInMinutes组合使用,不提供则为一次性闹钟

两个值得注意的点:

  • 最小值限制:表单下方特意标注了*提示——"Can be set to < 1 min in an unpacked extension, but not in a distributed CRX file."(在未打包的扩展中可设置为小于 1 分钟,但在分发的 CRX 文件中不可以)。也就是说,开发调试阶段(unpacked 加载)你可以用delayInMinutes: 0.1这类亚分钟值快速验证,但如果要发布到 Chrome Web Store,闹钟触发间隔(至少首次触发)必须不小于 1 分钟。这与when形式(毫秒级绝对时间)并不冲突,后者在调试时同样能实现秒级触发。
  • 周期为 0 即一次性闹钟:表单中period默认值为 0,代码通过if (period)判断,只有非零值才会写入periodInMinutes,从而保证一次性闹钟与周期闹钟的行为正确切换。

AlarmManager:封装闹钟 CRUD 与日志记录

页面逻辑的核心是AlarmManager类(定义于 index.js),它把chrome.alarms的所有操作封装为带日志输出的方法,并将触发日志渲染到页面的"Alarm log"区。这类"薄封装 + 日志"的做法非常适合学习:每一行 API 调用都有对应的可读日志,闹钟生命周期一目了然。

创建闹钟:createAlarm

// Thin wrapper around alarms.create to log creation event createAlarm(name, alarmInfo) { chrome.alarms.create(name, alarmInfo); const json = JSON.stringify(alarmInfo, null, 2).replace(/\s+/g, ' '); this.logMessage(`Created "${name}"\n${json}`); this.refreshDisplay(); }

create为异步操作,同名的闹钟会被新闹钟覆盖;重复创建同名闹钟时后创建的会替换先前的。创建后立即刷新显示区,让新闹钟立刻出现在列表中。

监听触发:onAlarm

构造函数中注册了chrome.alarms.onAlarm监听器:

constructor(display, log) { this.displayElement = display; this.logElement = log; this.logMessage('Manager: initializing demo'); this.displayElement.addEventListener('click', this.handleCancelAlarm); chrome.alarms.onAlarm.addListener(this.handleAlarm); }

handleAlarmasync箭头函数(this绑定到实例),收到alarm对象后把name与序列化后的完整 JSON 写入日志,并刷新闹钟列表:

handleAlarm = async (alarm) => { const json = JSON.stringify(alarm); this.logMessage(`Alarm "${alarm.name}" fired\n${json}}`); await this.refreshDisplay(); };

需要特别说明的是:在 MV3 中,chrome.alarms的主要使用场景正是 Service Worker 的定时唤醒——闹钟触发事件会唤醒休眠的 Service Worker,让扩展能在不常驻后台的情况下周期性地执行任务(例如清理缓存、轮询数据、发送通知)。本示例把onAlarm放在扩展页面中监听,是刻意为之的"页面内演示"方式,方便初学者直接观察触发过程;生产环境中的典型做法请参考下文"仓库中的更多实践"。

取消与清空:clear / clearAll

cancelAlarmcancelAllAlarms分别对应chrome.alarms.clear(name, callback)chrome.alarms.clearAll(callback)。它们的回调参数wasCleared(布尔值)用于区分"成功清除"与"闹钟不存在/已触发"两种结果,示例据此输出不同的日志:

async cancelAlarm(name) { return chrome.alarms.clear(name, (wasCleared) => { if (wasCleared) { this.logMessage(`Manager: canceled alarm "${name}"`); } else { this.logMessage(`Manager: could not canceled alarm "${name}"`); } }); } async cancelAllAlarms() { return chrome.alarms.clearAll((wasCleared) => { if (wasCleared) { this.logMessage(`Manager: canceled all alarms"`); } else { this.logMessage(`Manager: could not canceled all alarms`); } }); }

列表渲染与防抖刷新:getAll

populateDisplay调用chrome.alarms.getAll(callback)获取全部闹钟并逐个渲染成带"cancel"按钮的行:

async populateDisplay() { return chrome.alarms.getAll((alarms) => { for (const [index, alarm] of alarms.entries()) { const isLast = index === alarms.length - 1; this.renderAlarm(alarm, isLast); } }); }

refreshDisplay则用一个私有字段#refreshing实现简单的锁机制,防止并发刷新导致列表重复渲染:

#refreshing = false; async refreshDisplay() { if (this.#refreshing) { return; } // refresh in progress, bail this.#refreshing = true; // acquire lock try { await this.clearDisplay(); await this.populateDisplay(); } finally { this.#refreshing = false; // release lock } }

这里的#前缀是 ES2022 的私有类字段语法,finally确保锁在任何情况下都会释放。闹钟触发、创建、取消三个异步入口都可能触发refreshDisplay,这个锁有效避免了竞态条件下列表出现重复条目。

仓库中的更多实践:闹钟与通知、徽章联动

除了api-samples/alarms这个教学示例,仓库里还有多个以chrome.alarms为核心的实战样例,可作为扩展学习的第二站:

  • sample.water_alarm_notification:一个"喝水提醒"扩展。其 background.js 展示了纯 Service Worker 场景下的完整闭环——用户点击通知按钮后,从chrome.storage.sync读取提醒间隔并创建周期闹钟;闹钟触发时(onAlarm)清除 action 徽章并创建系统通知:
chrome.alarms.onAlarm.addListener(() => { chrome.action.setBadgeText({ text: '' }); chrome.notifications.create({ type: 'basic', iconUrl: 'stay_hydrated.png', title: 'Time to Hydrate', message: "Everyday I'm Guzzlin'!", buttons: [{ title: 'Keep it Flowing.' }], priority: 0 }); }); chrome.notifications.onButtonClicked.addListener(async () => { const item = await chrome.storage.sync.get(['minutes']); chrome.action.setBadgeText({ text: 'ON' }); chrome.alarms.create({ delayInMinutes: item.minutes }); });

注意这里的chrome.alarms.create({ delayInMinutes: item.minutes })省略了闹钟名称参数——此时闹钟使用默认名'',属于 API 的合法用法。

  • tutorial.mole-game 与 tutorial.open-api-reference、tutorial.quick-api-reference:分别在游戏控制器与 API 参考工具中通过onAlarm驱动 Service Worker 的周期性行为(如生成提示、刷新内容),印证了"alarms + Service Worker"是 MV3 定时任务的标准组合。

对比可见,api-samples/alarms的页面化展示更利于理解 API 每次调用的即时效果,而 water_alarm_notification 等样例更贴近真实产品形态,两者结合能帮你从"会用 API"进阶到"会设计定时任务架构"。

常见问题与注意事项

结合本示例的代码与 Chrome 的 API 约束,总结如下实践要点:

  1. 权限必须声明:忘记在 manifest.json 中添加"permissions": ["alarms"]是新手最常见的报错来源。
  2. whendelayInMinutes必须提供其一,否则chrome.alarms.create会报错;两者同时提供时以when为准。
  3. 分发的扩展闹钟间隔不能小于 1 分钟(CRX / Web Store 场景),unpacked 调试不受限;用when: Date.now() + delay可在调试时实现毫秒级触发。
  4. 同名闹钟会被覆盖:再次调用create时若名称相同,新配置会替换旧配置。
  5. 清除回调的wasCleared值得检查:它帮助区分"清除成功"与"该闹钟早已触发完(一次性闹钟触发后自动消失)"。
  6. MV3 中闹钟是 Service Worker 的唤醒机制:不要依赖页面存活来维持定时任务,onAlarm应注册在后台脚本中;本示例的页面内监听是教学演示,真实产品请参照 water_alarm_notification 的写法。

总结

api-samples/alarms用不到百行页面脚本完整呈现了chrome.alarms的五个核心 API(createclearclearAllgetAllonAlarm)与两种计时模型(when绝对时间 /delayInMinutes相对延迟),并通过AlarmManager类示范了如何将异步回调 API 封装为带日志、带防抖刷新、可测试的界面逻辑。结合仓库中 water_alarm_notification 等实战样例,你可以快速掌握"闹钟 + Service Worker + 通知"这一 MV3 定时任务最佳实践,并将其迁移到自己的扩展中。

  • 示例工程

【免费下载链接】chrome-extensions-samples

Chrome Extensions Samples

项目地址:https://gitcode.com/gh_mirrors/ch/chrome-extensions-samples
点击查看免费下载
上一篇:终极MonkeyDev教程:零基础实现非越狱iOS设备的微信自定义功能
下一篇:突破直播瓶颈:Owncast启用Intel Quick Sync加速编码的完整指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询