Screen Wake Lock API:不用套件也能讓頁面保持亮著
整理 Screen Wake Lock API 的基本用法,說明 HTTPS、頁面可見性、切換分頁後重新取得 wake lock,以及 Vue/Nuxt 計時器使用時的注意事項。
W
以前常用 nosleep.js 這類套件,透過播放隱藏影片,避免手機螢幕自動休眠。現在瀏覽器已經有正式的 Screen Wake Lock API:
const wakeLock = await navigator.wakeLock.request('screen')
不用另外安裝套件。
使用條件
使用前有幾件事要注意:
- 必須在 HTTPS 或 localhost 等 secure context
- 頁面必須處於可見狀態
- 瀏覽器必須支援
navigator.wakeLock
可以先做 feature detection:
if ('wakeLock' in navigator) {
// 支援 Screen Wake Lock API
}
目前 Screen Wake Lock 只有 screen 類型,用途是避免螢幕自動變暗或鎖定。
基本用法
取得 wake lock:
let wakeLock = null
async function keepAwake() {
try {
wakeLock = await navigator.wakeLock.request('screen')
} catch (error) {
console.warn('無法取得 Wake Lock', error)
}
}
不需要時要主動釋放:
async function allowSleep() {
await wakeLock?.release()
wakeLock = null
}
例如計時器開始時取得:
keepAwake()
停止時釋放:
allowSleep()
切換分頁後要重新取得
Wake Lock 有一個很容易漏掉的地方:
當頁面進入背景時,瀏覽器可能會自動釋放 wake lock。
因此使用者切到其他 App 或分頁,再切回來時,需要重新申請。
let shouldKeepAwake = false
let wakeLock = null
async function keepAwake() {
shouldKeepAwake = true
try {
wakeLock = await navigator.wakeLock.request('screen')
} catch {
wakeLock = null
}
}
async function allowSleep() {
shouldKeepAwake = false
await wakeLock?.release()
wakeLock = null
}
document.addEventListener('visibilitychange', async () => {
if (
shouldKeepAwake &&
document.visibilityState === 'visible'
) {
await keepAwake()
}
})
這裡另外使用 shouldKeepAwake 記錄「使用者是否仍然希望保持亮著」。
否則如果使用者已經停止計時器,切一次分頁回來又重新取得 wake lock,就會變成錯誤的行為。
Nuxt / Vue 使用
Nuxt 有 SSR,所以不能在伺服器端直接存取 navigator。
可以簡單包成 composable:
// app/composables/useWakeLock.ts
export function useWakeLock() {
const isActive = ref(false)
let wakeLock: WakeLockSentinel | null = null
async function request() {
if (
!import.meta.client ||
!('wakeLock' in navigator)
) {
return
}
try {
wakeLock = await navigator.wakeLock.request('screen')
isActive.value = true
} catch {
isActive.value = false
}
}
async function release() {
isActive.value = false
await wakeLock?.release()
wakeLock = null
}
async function handleVisibilityChange() {
if (
isActive.value &&
document.visibilityState === 'visible'
) {
await request()
}
}
onMounted(() => {
document.addEventListener(
'visibilitychange',
handleVisibilityChange
)
})
onUnmounted(() => {
document.removeEventListener(
'visibilitychange',
handleVisibilityChange
)
release()
})
return {
isActive,
request,
release
}
}
使用:
<script setup lang="ts">
const {
isActive,
request,
release
} = useWakeLock()
function startTimer() {
request()
}
function stopTimer() {
release()
}
</script>
Wake Lock 不代表計時器一定準
Screen Wake Lock 的功能只有:
防止螢幕自動變暗或鎖定。
它不會保證背景分頁裡的 JavaScript 持續每秒執行。
因此計時器不要單純這樣累加:
// 不建議
setInterval(() => {
remaining -= 1
}, 1000)
背景分頁的 timer 可能被瀏覽器降頻。
比較安全的方式是記錄結束時間:
const endAt = Date.now() + duration
setInterval(() => {
remaining = Math.max(
0,
endAt - Date.now()
)
}, 250)
即使中間更新被暫停,重新執行時仍然會根據目前時間計算正確的剩餘時間。
所以兩件事要分開理解:
Screen Wake Lock
→ 讓螢幕保持亮著
時間戳
→ 讓計時結果保持正確
還需要 nosleep.js 嗎?
現代瀏覽器可以優先使用原生 Screen Wake Lock API。
建議做 feature detection:
const isSupported =
'wakeLock' in navigator
如果不支援,讓功能正常降級即可。
對倒數計時器來說,Wake Lock 通常只是額外的使用體驗,不應該影響計時器本身能不能使用。
小結
使用 Screen Wake Lock API 最重要的是記住:
// 保持螢幕亮著
const wakeLock =
await navigator.wakeLock.request('screen')
// 恢復正常休眠
await wakeLock.release()
另外兩個常見注意事項:
切到背景
→ wake lock 可能被自動釋放
→ 回到頁面後重新 request()
計時器
→ 不要依賴 setInterval 累加
→ 用時間戳重新計算
在 Nuxt 裡再注意 SSR 階段沒有 navigator,使用 import.meta.client 或在 client lifecycle 裡處理即可。