跳到主要内容

Composables

What is a "Composable"?

In the context of Vue applications, a "composable" is a function that leverages Vue's Composition API to encapsulate and reuse stateful logic.

Mouse Tracker Example

// event.js
import { onMounted, onUnmounted } from 'vue'

export function useEventListener(target, event, callback) {
// if you want, you can also make this
// support selector strings as target
onMounted(() => target.addEventListener(event, callback))
onUnmounted(() => target.removeEventListener(event, callback))
}

// mouse.js
import { ref } from 'vue'
import { useEventListener } from './event'

export function useMouse() {
const x = ref(0)
const y = ref(0)

useEventListener(window, 'mousemove', (event) => {
x.value = event.pageX
y.value = event.pageY
})

return { x, y }
}
<script setup>
import { useMouse } from './mouse.js'

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

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

Async State Example

toValue() is an API added in 3.3. It is designed to normalize refs or getters into values. If the argument is a ref, it returns the ref's value; if the argument is a function, it will call the function and return its return value. Otherwise, it returns the argument as-is

// 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))
.then((res) => res.json())
.then((json) => (data.value = json))
.catch((err) => (error.value = err))
}

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

return { data, error }
}

Conventions and Best Practices

Input Arguments

If your composable creates reactive effects when the input is a ref or a getter, make sure to either explicitly watch the ref / getter with watch(), or call toValue() inside a watchEffect() so that it is properly tracked

Return Values

The recommended convention is for composables to always return a plain, non-reactive object containing multiple refs

// x and y are refs
const { x, y } = useMouse()

If you prefer to use returned state from composables as object properties, you can wrap the returned object with reactive() so that the refs are unwrapped

const mouse = reactive(useMouse())
// mouse.x is linked to original ref
console.log(mouse.x)