Vue3 使用hooks优化需要频繁使用定时器的场景

场景描述:

在做数据大屏项目过程中,一个页面中可能有许多图表组件,每个图表组件需要间隔不同的时间去刷新数据。

如果每个组件中都写一个定时器去刷新数据,总感觉不是很优雅,会写很多重复的代码,于是我打算用hooks去抽离出这部分重复的代码。

思路:

1. 全局只使用一个定时器,每隔1s向外通知一个事件,在需要定时刷新数据的组件中监听到事件后自增一个秒数,用 (自增秒 % 需要几秒更新 === 0)就可以判断是否到达一次间隔的时间,从而执行刷新数据的方法,然后就是需要将上述逻辑抽离到hooks函数中。
第一步:定义全局事件mitt
// @/utils/mitter.js

// mitt事件总线
import mitt from 'mitt'
const mitter = mitt()
export default mitter
第二步:定义全局定时器
// @/utils/timer.js

import mitter from '@/utils/mitter'

 // 全局定时器,每1秒向外面广播时间更新事件
const timer = setInterval(() => {
  mitter.emit('time_update')
}, 1000)

export default timer
使用定时器 (建议在App.vue中使用)
// App.vue 中引入timer
import timer from '@/utils/timer'
import { onBeforeUnmount } from 'vue'
export default {
  setup() {
    onBeforeUnmount(() => timer && clearInterval(timer))
  }
}
第三步:定义 useTimeExecutor.js 定时执行器hooks
// @/hooks/useTimeExecutor.js

import { ref, onMounted, onBeforeUnmount, onActivated, onDeactivated } from 'vue'
import mitter from '@/utils/mitter'

/**
 * 统一处理定时函数的hooks
 * @param options: Object
 * options.second: Number      请求的间隔秒
 * options.func: Function      需要执行的函数
 * options.immediate: Boolean  是否立即执行
 * options.wait: Boolean       是否等待执行(如果上一个loading未执行完,就不会执行)
 * options.disabled: Boolean   是否禁用(如果希望手动执行exec就开启此选项)
 */
export default function (options = {}) {
  const {
    second = 5,
    immediate = true,
    wait = true,
    disabled = false,
    func = function () {}
  } = options

  let current = 0
  let isListening = false
  const isPause = ref(false)
  const isLoading = ref(false)

  // 页面挂载时执行一次,且开启监听
  onMounted(() => {
    immediate && exec()
    on()
  })

  // 使用keep-alive页面激活时开启监听
  onActivated(() => on())

  // 开启监听
  const on = () => {
    if (!disabled && !isListening) {
      isListening = true
      mitter.on('time_update', onTime)
    }
  }

  // 关闭监听
  const off = () => {
    mitter.off('time_update', onTime)
    isListening = false
  }

  // 监听到时间改变, 当满足时间间隔时执行函数
  const onTime = () => {
    if (isPause.value) return
    current++
    if (current % second === 0) exec()
  }

  // 暂停执行
  const pause = () => (isPause.value = true)

  // 继续执行
  const play = () => (isPause.value = false)

  // 执行函数
  const exec = async () => {
    if (wait && isLoading.value) return
    isLoading.value = true
    let res
    try {
      res = await func()
    } catch (e) {
      throw new Error(e) // 可统一处理错误
    } finally {
      isLoading.value = false
    }
    return res
  }

  // 页面卸载时销毁监听
  onBeforeUnmount(() => off())

  // 使用keep-alive页面隐藏时销毁监听
  onDeactivated(() => off())

  return {
    play,
    pause,
    exec,
    isLoading
  }
}
在需要定时刷新数据的组件中使用:
import { defineComponent, ref } from 'vue'
import useTimeExecutor from '@/hooks/useTimeExecutor'

export default defineComponent({
  setup() {
    const data = ref({})

    // 更新数据的方法
    const fetchData = async () => {
      const res = await http.get('/api/some/data')
      data.value = res
    }

    // 使用hooks, 表示每隔10秒会执行fetchData去更新数据
    const { pause, play, isLoading } = useTimeExecutor({
      second: 10,
      func: fetchData
    })

     // 可自行暂停/继续更新数据
    const handleButtonClick = e => {
      e ? pause() : play()
    }

    return {
      data,
      isLoading ,
      handleButtonClick 
    }
  }
})

总结

  1. 代码抽离复用后,我们的组件中减少了许多样板代码,只需要使用useTimeExecutor就能让组件中的函数拥有定时请求的功能,并且可以随时暂停/继续更新,不用频繁地写定时器和清除定时器了,一切的逻辑都在useTimeExecutor函数钩子中处理了,这也是组合式API + Hooks对代码复用逻辑抽离的优点的体现。

  2. 如果代码发现可优化欢迎指正😁。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容