Skip to content

组合式函数(Hooks)

组合式函数 是一个利用 Vue 的组合式API 来封装和复用有状态逻辑的函数

鼠标跟踪器示例

ts
import { ref, onMounted, onUnmounted } from 'vue'

// 通常hooks命名以 use 开头
export function useMouse() {
  const x = ref(0)
  const y = ref(0)

  function update(event) {
    x.value = event.pageX
    y.value = event.pageY
  }

  onMounted(() => window.addEventListener('mousemove', update))
  onUnmounted(() => window.removeEventListener('mousemove', update))

  return { x, y }
}
vue
<template>
	Mouse position is at: {{ mouseX }}, {{ mouseY }}
</template>

<script setup lang="ts">
  import { useMouse } from './useMouse.ts'
  
  const x = ref(-10)

  // 当 hooks 中解构的变量与组件中变量名称冲突时,可以重命名
  const { x: mouseX, y: mouseY } = useMouse()
</script>

异步状态示例

特定场景下,可能需要向 hooks 传递参数,看下面的示例:

ts
import { ref } from 'vue'

export function useFetch(url) {
  const data = ref(null)
  const error = ref(null)

  fetch(url)
    .then((res) => res.json())
    .then((json) => (data.value = json))
    .catch((err) => (error.value = err))

  return { data, error }
}
vue
<script setup lang="ts">
  import { useFetch } from './useFetch.ts'

  const { data, error } = useFetch('/api/getUsers')
</script>

接收响应式状态

useFetch() 接收一个静态URL字符串作为输入——因此它只会执行一次 fetch 并且就此结束。

如果我们想要在 URL 改变时重新 fetch 呢?

需要将响应式状态传入组合式函数,并让它基于传入的状态来创建执行操作的侦听器。

例如,useFetch() 应该能够接收一个 ref:

ts
const url = ref('/api/getUsers')

const { data, error } = useFetch(url)

// 更新 url 的值,希望能够重新触发fetch
url.value = '/api/getUsers?id=1'

或者接受一个 getter 函数:

ts
// 当 props.id 改变时重新 fetch
const { data, error } = useFetch(() => `/posts/${props.id}`)

可以用 watchEffect()toValue() API 来重构现有的实现:

ts
import { ref, watchEffect, toValue } from 'vue'

export function useFetch(url) {
  const data = ref(null)
  const error = ref(null)

  const fetchData = () => {
    // reset state before fetching..
    data.value = null
    error.value = null

    fetch(toValue(url))
      .then((res) => res.json())
      .then((json) => (data.value = json))
      .catch((err) => (error.value = err))
  }

  watchEffect(() => {
    // 侦听器监听了 toValue(url) 的值
    fetchData()
  })

  return { data, error }
}

提示

toValue() 是一个在 3.3 版本中新增的 API,它的设计目的是将 ref 或 getter 规范化为值:

  • 如果参数是 ref,它会返回 ref 的值;
  • 如果参数是 getter 函数,它会调用函数并返回其返回值;
  • 否则,它会原样返回参数;
ts
// 推荐做法
import { toValue } from 'vue'

function useFeature(maybeRefOrGetter) {
  // 如果 maybeRefOrGetter 是一个 ref 或 getter,将返回它的规范化值。否则原样返回。
  const value = toValue(maybeRefOrGetter)
}

Released under the MIT License.