Skip to content

基本概念

“组合式函数”(Composables) 是一个利用 Vue 的组合式 API 来封装和复用有状态逻辑的函数,负责管理会随时间而变化的状态组合式逻辑

  • 封装逻辑:可以将与某一功能相关的逻辑封装在一个函数中(通常被称为“组合函数”)。
  • 逻辑复用:这些组合函数可以在多个组件中复用,不需要每次都在组件内重复相同的逻辑。
  • 模块化和解耦:不同的功能逻辑可以分开维护,使得组件代码更加易读、易维护。

其实基本上可以认为就是 hook,起名为 useXXX定义和导出组合式API(Composition API)的自定义函数)。

参数

参数可以是 ref,getter,或者其他任何东西,可以通过 toValue 统一处理它们。

返回值

返回值推荐使用 ref 而不是 reactive。 因为 reactive 只对整个对象的引用保持响应性,而解构后拿到的是属性没有这种特性。

副作用

要在组件卸载的时候调钩子函数清除副作用!

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

鼠标跟踪器

为了创建一个可复用的鼠标跟踪逻辑,采用 hook 分离逻辑:

ts
// mouse.js
import { ref, onMounted, onUnmounted } from 'vue'

// 按照惯例,组合式函数名以“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 }
}

注意这里返回的是两个响应式对象。由于 JS 闭包机制,即使函数执行完成,只要对内部成员的引用存在,依旧可以访问它们,因此这里返回引用后数值依旧可以正常被更新:

html
<script setup>
import { useMouse } from './mouse.js'

const { x, y } = useMouse()
</script>

<template>Mouse position is at: {{ x }}, {{ y }}</template>

异步状态

promise

一个代理,代表一个在创建 promise 时不一定已知的值,将处理程序与异步操作的最终成功值或失败原因关联起来,使得异步方法可以像同步方法一样返回值:异步方法不会立即返回最终值,而是返回一个 promise,以便在将来的某个时间点提供该值。 其拥有以下状态:

  • 待定(pending):初始状态,既没有被兑现,也没有被拒绝。
  • 已兑现(fulfilled):意味着操作成功完成。
  • 已拒绝(rejected):意味着操作失败。 当 promise 进行链式调用时,上一个 then 返回的结果会自动作为下一个 then 的参数,类似这样:(promise D, (promise C, (promise B, (promise A) ) ) )
ts
// fetch.js
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)) //解析出json内容并赋值给ref
    .catch((err) => (error.value = err))

  return { data, error }
}
html
<script setup>
import { useFetch } from './fetch.js'
const { data, error } = useFetch('...')
</script>

接收响应式状态

我们希望 useFetch 在参数值改变时重新调用,这时候应当向其中传递 ref 对象或一个 getter。

ts
// fetch.js
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)) 
	  //返回调用getter返回值的值,或是ref的.value,如果都不是就返回参数的值
      .then((res) => res.json())
      .then((json) => (data.value = json))
      .catch((err) => (error.value = err))
  }

  watchEffect(() => {
    fetchData()
  })

  return { data, error }
}

watchEffect 可以立即运行一个函数,同时响应式地追踪其(响应式)依赖,并在依赖更改时重新执行。在本例中是 url 的改变触发了这个侦听器,并触发副作用函数 fetchDatawatchEffect 是一个响应式侦听器,它会自动跟踪在其回调中使用的所有响应式数据(如 url)。当这些数据变化时,它会重新运行回调,而不会创建新的侦听器。Vue 会确保只触发一个现有的 watchEffect,而不会累积多个。