在 Web 应用中使用 Battery Status API
大多数 Web 应用无法知道使用者是否在火车上电量只剩 8%,而且身边没有充电器。Battery Status API 改变了这一点——至少在某些浏览器中是这样。它允许你的 JavaScript 读取设备的电池电量、充电状态和预计剩余时间,从而可以相应地调整应用的行为。
本文将解释该 API 的工作原理、你可以用它实际做什么,以及为什么需要谨慎对待使用它的场景和方式。
核心要点
- Battery Status API 通过
navigator.getBattery()暴露电池电量、充电状态和时间估算,返回一个包含四个可读属性和四个对应变化事件的BatteryManager对象。 - 在调用 API 之前始终使用特性检测和错误处理,因为它仅在安全上下文(HTTPS)中可用,并且仅在基于 Chromium 的浏览器中可用。
- Firefox 出于指纹识别担忧移除了该 API,Safari 从未实现过它,因此请将电池感知行为严格视为渐进式改进——绝不能作为核心功能。
- 实际应用包括暂停后台同步、减少动画效果,以及在电量低时提示用户保存工作。
什么是 Battery Status API?
Battery Status API 通过 navigator.getBattery() 方法暴露电池信息,该方法返回一个 Promise,解析为 BatteryManager 对象。该对象提供四个属性:
level— 介于0和1之间的数字(例如,0.72表示 72%)charging— 如果设备正在充电则为true,否则为falsechargingTime— 充满电的预计秒数dischargingTime— 电池耗尽的预计秒数
两个时间属性都可能返回 Infinity — 当设备是没有电池的台式机时,当估算尚未准备好时,或者当浏览器为了限制指纹识别风险而故意隐藏精度时。
如何安全使用 navigator.getBattery()
在调用 API 之前始终检查支持情况。它仅在安全上下文(HTTPS)中可用,即使如此,也仅在暴露它的浏览器中可用。
在嵌入式上下文中,访问也可能受到 battery Permissions Policy 的限制。
async function initBatteryMonitor() {
// 特性检测 — 不要假设 API 存在
if (!('getBattery' in navigator)) {
console.warn('此浏览器不支持 Battery Status API。')
return
}
let battery
try {
battery = await navigator.getBattery()
} catch (err) {
console.error('无法访问电池信息:', err)
return
}
// 读取初始值
console.log(`电量: ${battery.level * 100}%`)
console.log(`充电中: ${battery.charging}`)
// 在显示前优雅地处理 Infinity
const formatTime = (seconds) =>
seconds === Infinity ? '未知' : `${Math.round(seconds / 60)} 分钟`
console.log(`充电时间: ${formatTime(battery.chargingTime)}`)
console.log(`放电时间: ${formatTime(battery.dischargingTime)}`)
// 监听变化
battery.addEventListener('levelchange', () => {
console.log(`电池电量已更新: ${battery.level * 100}%`)
})
battery.addEventListener('chargingchange', () => {
console.log(`充电状态已改变: ${battery.charging}`)
})
}
initBatteryMonitor()
你可以监听的四个事件是 levelchange、chargingchange、chargingtimechange 和 dischargingtimechange。每个事件在其对应的值发生变化时触发。
实际用例
当你想要为低电量用户减少资源消耗时,该 API 最有用:
- 当
battery.level降至0.2等阈值以下时暂停后台同步或轮询 - 降低动画复杂度或禁用自动播放视频
- 在电池耗尽前提示用户保存工作
- 调整 PWA 行为 — 例如,推迟非关键网络请求
这些都是渐进式改进。你的应用在没有它们的情况下也应该正常工作。
Discover how at OpenReplay.com.
浏览器支持和隐私问题
这是 Battery Status API 变得复杂的地方。
| 浏览器 | 支持情况 | 备注 |
|---|---|---|
| 基于 Chromium 的浏览器 | 支持但可用性有限 | 仅限 HTTPS;值可能被减少、四舍五入或替换为默认值 |
| Firefox | 否 | 由于隐私问题已移除 |
| Safari | 否 | 从未实现 |
当前的浏览器支持范围很窄。Firefox 移除了该 API,因为研究人员证明它可以用作指纹识别向量 — 电池电量和时间值的组合足够具体,可以帮助跨站点跟踪用户。作为回应,基于 Chromium 的浏览器可能会降低精度或暴露默认值以限制该风险。
这意味着你永远不应该将 Battery API 作为主要功能依赖。将其视为锦上添花的改进,并始终为它返回空值时编写回退行为。
在开发中测试 Battery API
Chrome DevTools 目前在传感器(Sensors)下没有提供内置的电池模拟器。要在开发期间测试与电池相关的行为,你有几个实用选项:
- 使用真实移动设备通过 USB 远程调试连接,并观察实际的电池状态变化。
- 在代码中模拟 API,通过用返回包含所需属性和事件的假
BatteryManager对象的函数替换navigator.getBattery。 - 使用浏览器覆盖或测试库,如 Sinon 或 Jest,在测试套件中存根
navigator.getBattery()。
以下是一个可以放入开发环境的最小模拟:
function mockBattery({ level = 0.15, charging = false } = {}) {
const fake = {
level,
charging,
chargingTime: charging ? 3600 : Infinity,
dischargingTime: charging ? Infinity : 1800,
addEventListener: () => {},
}
navigator.getBattery = () => Promise.resolve(fake)
}
// 模拟低电量、未充电的设备
mockBattery({ level: 0.08, charging: false })
这种方法让你完全控制代码接收的值,而不依赖于浏览器特定的工具。
结论
Battery Status API 是一个小众工具,具有实际限制。浏览器支持范围很窄,值故意不精确,隐私影响意味着其未来不确定。但在受控环境中——内部工具、针对已知基于 Chromium 浏览器的 PWA,或自助服务终端式应用——它可以显著改善用户体验。
使用特性检测,明确处理 Infinity 值,在不再需要时清理事件监听器,并且永远不要在其基础上构建关键功能。
常见问题
仅在基于 Chromium 的移动浏览器(如 Android 版 Chrome)上可以,即使在那里,可用性也可能因环境而异。iOS 上的 Safari 从未实现过它,Firefox 完全移除了支持。在调用 navigator.getBattery() 之前,始终使用特性检测来检查可用性,并为不支持的浏览器提供回退行为。
可能存在。研究人员表明,电池电量和放电时间值的组合足够精确,可以跨网站对用户进行指纹识别。这导致 Firefox 完全移除了该 API。基于 Chromium 的浏览器可能会降低精度或暴露默认值以限制该风险,但这一担忧仍然是某些浏览器避免实现它的原因。
存储对处理函数的引用,并在不再需要时调用 removeEventListener,例如当组件卸载时。由于 BatteryManager 对象持续存在,未能移除监听器可能会导致内存泄漏或在应用多次重新初始化监视器时出现意外行为。
它在支持的基于 Chromium 的桌面浏览器中工作,但值可能没有意义。没有电池的台式机通常报告电量为 1,充电状态为 true,两个时间属性都设置为 Infinity。该 API 在电池状态实际波动的笔记本电脑和移动设备上最有用。
Complete picture for complete understanding
Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.
Check our GitHub repo and join the thousands of developers in our community.