Skip to page content

useIdle 组合式 API
v2.34+

useIdle() composable 会在用户停止与你的应用交互时告诉你:在给定的一段时间内没有鼠标移动、点击、按键、触摸或滚轮操作。它的典型用途包括:在无人观看时暂停轮询或动画、在会话即将过期、用户被登出之前发出警告,或者让自助终端(kiosk)的屏幕变暗。

它监听 document 上的活动事件,因此无论你的组件渲染在哪里,它都能覆盖整个页面。

TIP

在 SSR 或 SSG 模式的服务端,该 composable 不会追踪任何东西,isIdle 始终保持为 false。

在组件之外使用

该 composable 也可以在 setup() 之外调用:在 boot 文件、store 或普通模块中。此时它会立即开始追踪,并且不会自行停止:完成后请调用 stopIdle()。

语法

import { useIdle } from 'quasar'

setup () {
  const { isIdle, lastActive, resetIdle, stopIdle } = useIdle({
    // all optional:
    timeout: 60000,  // ms of inactivity before the user counts as idle
    events: [        // the events (on document) that count as an activity
      'mousemove', 'mousedown', 'keydown', 'touchstart', 'wheel'
    ],
    disabled: false, // pause tracking
    onIdle (isIdle) { // called on every transition
      // ...
    }
  })

  // ...
}
function useIdle(
  options?: MaybeRefOrGetter<{
    timeout?: number
    events?: string[]
    disabled?: boolean
    onIdle?: (isIdle: boolean) => void
  }>
): {
  isIdle: Ref<boolean>
  lastActive: Ref<number>
  resetIdle: () => void
  stopIdle: () => void
}

一旦 timeout 毫秒内没有任何 events 触发,isIdle 就会变为 true,而在紧接着的下一次活动时又回到 false。每一次这样的状态切换都会以新的值调用 onIdle 回调(初始的活动状态不会触发),因此你无需自己去 watch 这个 ref。lastActive 保存了最后一次活动的时间戳(相当于 Date.now() 得到的值),你可以用它来展示「离开时长」,或计算距离用户进入空闲状态还剩多少时间。

这些活动事件是在捕获阶段(capture phase)监听的,因此即便某个处理函数阻止了事件传播,活动依然会被计入;并且这些监听器是被动(passive)的,绝不会拖慢滚动。一次活动本身开销很小:只是记录一个时间戳,别无其他。内部计时器每个周期只装填(arm)一次,触发时才检查已过去的时间,因此连续的 mousemove 事件流并不会反复重置计时器。

页面处于隐藏状态(切到其他标签页、窗口被最小化)时所度过的时间会被计为不活动。浏览器会限制被隐藏页面的计时器,因此该 composable 会在页面重新可见的第一时间把状态确定下来。

resetIdle() 相当于你手动制造了一次活动,用于那些事件无法感知到的交互(例如通过 websocket 收到一条你认为足以维持会话活跃的消息,或者一段仍在播放的视频)。stopIdle() 会彻底结束追踪;你很少会用到它,因为组件销毁时该 composable 会自行停止。

运行时更改选项

选项可以是一个普通对象、一个 Ref 或一个 getter 函数。普通对象只读取一次。使用 Ref 或 getter 时,该 composable 会追踪它所读取的任何响应式状态,并在该状态变化时重新应用:

  • 切换 disabled 会暂停追踪(被暂停的用户永远不会进入空闲),重新启用时会从那一刻起开始一个全新的计时周期
  • 更改 timeout 会立即重新评估状态:更短的值可能会让用户当场进入空闲,更长的值可能会让他们重新变为活动
  • 更改 events 会替换掉相应的监听器
import { ref } from 'vue'
import { useIdle } from 'quasar'

setup () {
  const loggedIn = ref(false)

  const { isIdle } = useIdle(() => ({
    timeout: 5 * 60 * 1000,
    disabled: !loggedIn.value
  }))

  // ...
}

示例

下面这个示例演示了 useIdle() 的基本用法:停止移动鼠标、打字或触摸页面 5 秒后,状态就会从 active 变为 idle,你还可以切换开关来暂停追踪。

<template>
  <div class="q-pa-md">
    <div class="row items-center q-gutter-sm q-mb-md">
      <q-btn color="primary" label="resetIdle()" no-caps @click="resetIdle" />
      <q-toggle v-model="disabled" label="Disable tracking" />
    </div>

    <div class="row items-center q-gutter-sm">
      <q-badge
        :color="isIdle ? 'orange' : 'positive'"
        :label="isIdle ? 'idle' : 'active'"
      />
      <div v-if="elapsed !== null">Last activity: {{ elapsed }}s ago</div>
    </div>

    <div class="text-caption q-mt-sm">
      Stop moving the mouse, typing or touching the page for 5 seconds.
    </div>
  </div>
</template>

<script setup>
import { onMounted, ref } from 'vue'
import { useIdle, useInterval } from 'quasar'

const disabled = ref(false)

const { isIdle, lastActive, resetIdle } = useIdle(() => ({
  timeout: 5000,
  disabled: disabled.value
}))

const elapsed = ref(null)
const { registerInterval } = useInterval()

onMounted(() => {
  registerInterval(() => {
    elapsed.value = Math.round((Date.now() - lastActive.value) / 1000)
  }, 1000)
})
</script>

在会话即将过期时向用户发出警告,而这个警告会在用户重新交互的一瞬间自动消失:

import { ref } from 'vue'
import { useIdle } from 'quasar'

setup () {
  const showWarning = ref(false)

  useIdle({
    timeout: 10 * 60 * 1000,
    onIdle (isIdle) {
      showWarning.value = isIdle
    }
  })

  // ...
}
TIP

如果你想仅仅对页面本身的隐藏或显示做出反应(而不关心用户是否在活动),请使用 AppVisibility 插件。