美文网首页
Vue3 使用hooks优化需要频繁使用定时器的场景

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

作者: pjqdyd | 来源:发表于2022-01-08 01:21 被阅读0次

场景描述:

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

如果每个组件中都写一个定时器去刷新数据,总感觉不是很优雅,会写很多重复的代码,于是我打算用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. 如果代码发现可优化欢迎指正😁。

相关文章

网友评论

      本文标题:Vue3 使用hooks优化需要频繁使用定时器的场景

      本文链接:https://www.haomeiwen.com/subject/dogjcrtx.html