开发过程中经常遇到定时调用的场景,比如倒计时,比如每隔多少秒调用一次接口刷新数据,再比如实现一个轮播图片的功能,还有实现轨迹播放的时候也会用到。在JS中我们使用setInterval来开启定时器,使用clearInterval来清除一个定时器。在使用vue作为前端框架开发时,我们往往需要在组件或者页面卸载的时候清除定时器,在实现播放器功能时需要暂停和唤醒定时器。今天要学习的useIntervalFn真的很不错,把自动清除还有暂停和唤醒都帮我们实现了,我们一起看一下它是如何办到的吧!
1.示例
官方文档示例代码:
<script setup lang="ts">
import { ref } from 'vue'
import { useIntervalFn } from '@vueuse/core'
import { rand } from '@vueuse/shared'
const greetings = ['Hello', 'Hi', 'Yo!', 'Hey', 'Hola', 'こんにちは', 'Bonjour', 'Salut!', '你好']
const word = ref('Hello')
const interval = ref(500)
const { pause, resume, isActive } = useIntervalFn(() => {
word.value = greetings[rand(0, greetings.length - 1)]
}, interval)
</script>
<template>
<p>{{ word }}</p>
<p>
interval:
<input v-model="interval" type="number" placeholder="interval">
</p>
<button v-if="isActive" class="orange" @click="pause">
Pause
</button>
<button v-if="!isActive" @click="resume">
Resume
</button>
</template>
上述代码演示了借助useIntervalFn实现了每隔500ms实现一次问候,问候语为从greetings数组中取得随机值。
可以点击Pause暂停问候。
可以点击Resume唤起计时器继续问候。
2.源码分析
一张图片概览源码:
2.1 IntervalFnOptions接口说明
export interface IntervalFnOptions {
immediate?: boolean
immediateCallback?: boolean
}
immediate表示是否立即开启timer,默认值为true; immediateCallback表示是否立刻执行回调,默认值为false。
2.2 参数处理及初始化
const {
immediate = true,
immediateCallback = false,
} = options
let timer: any = null
const isActive = ref(false)
获取options参数的选项immediate和immediateCallback,如果用户没有传options参数或者相应的选项则使用默认值,分别是true和false。初始化timer为null,isActive为false。isActive表示timer是否是激活状态。
2.3 clean()清除timer
function clean() {
if (timer) {
clearInterval(timer)
timer = null
}
}
clean方法中对timer进行判断,然后调用clearInterval清除定时器,最后把timer赋值为null。
2.4 pause()暂停定时器
function pause() {
isActive.value = false
clean()
}
pause()方法用于暂停定时器,首选将isActive设置为false,然后调用clean()方法清空定时器。
2.5 resume() 唤醒定时器
function resume() {
if (unref(interval) <= 0)
return
isActive.value = true
if (immediateCallback)
cb()
clean()
timer = setInterval(cb, unref(interval))
}
resume用于唤醒定时器。如果时间间隔interval小于零则返回;否则开始执行定时器,将isActive设置为true,然后根据immediateCallback判断是否立即执行一次回调;在执行之前要先调用一次clean,然后调用setInterval方法启动定时器。这里需要注意interval可能是一个ref的响应式变量,所以这里调用unref。
2.6 立即启动定时器
if (immediate && isClient)
resume()
如果immediate为true并且isClient为true则调用resume()启动定时器。
2.7 监听interval和注册pause到当前活跃effect作用域
if (isRef(interval)) {
const stopWatch = watch(interval, () => {
if (immediate && isClient)
resume()
})
tryOnScopeDispose(pause)
}
tryOnScopeDispose(pause)
如果interval是响应式的则需要监听interval的变化,在interval发生变化时调用resume方法重新启动定时器;watchAPI的返回值是stopWatch,把stopWatch()方法注册到当前活跃的effect作用域上。如果interval不是响应式的则将pause方法注册到当前活跃的effect作用域上。在笔者之前的文章useEventListener:一个减少vue开发者心智负担的事件监听方法 中有介绍过,像stopWatch或者pause这样的回调函数会在相关的effect作用域结束之后被调用,从而实现注销事件或者清除定时等功能。
2.8 返回值
return {
isActive,
pause,
resume,
}
将定时器状态isActive以及停止和唤醒定时器的方法pause以及resume方法返回。
3.总结
useIntervalFn的实现中使用到vue3 的API有isRef, ref, unref, watch;使用JS原生的API有setInterval和clearInterval。在实现细节中需要注意的地方有:参数interval可能是一个ref,所以需要unref获取其原始值;如果interval是ref那么还需要使用watch对其监听;需要将pause或者把stopWatch注册到当前活跃的effect作用域上为的是自动调用实现清除定时器的功能。
4.留给读者
读完本文的朋友们可以留言回答如下问题:
1.在你的开发过程中都有哪些功能或者场景用到了定时调用?
2.你在使用完通过setInterval声明的定时器后会调用clearInterval清除定时器吗?
3.你认为useIntervalFn的源码中有哪些可以借鉴之处?