Composables
Composables are a core and powerful concept in Vue's Composition API. It's a pattern that uses Vue's reactivity API to encapsulate and reuse stateful logic.
What is "Stateful Logic"?
Stateful logic refers to logic that changes over time. For example:
- Tracking the mouse position on the page.
- Fetching data from a server and managing its loading and error states.
- Listening to changes in window size.
In the Options API, we usually use Mixins to reuse this logic, but Mixins have some issues like naming conflicts and unclear sources. Composables provide a clearer, more flexible solution.
Conventions and Rules
A composable is a regular JavaScript function, but it follows some conventions:
- Naming: Usually starts with
use, for exampleuseMouse(),useFetch(). This is a convention that tells other developers this function uses Vue's reactivity API internally. - Input: Can accept reactive
reforreactiveobjects as parameters and be able to listen to their changes. - Output: Always returns a regular object containing multiple reactive
refs for convenient destructuring in components.
Example: useMouse.js
Let's create a classic composable to track mouse coordinates.
src/composables/useMouse.js
import { ref, onMounted, onUnmounted } from 'vue'
// By convention, function name starts with `use`
export function useMouse() {
// The "state" being encapsulated and managed
const x = ref(0)
const y = ref(0)
// Composables can use lifecycle hooks just like components
function update(event) {
x.value = event.pageX
y.value = event.pageY
}
onMounted(() => window.addEventListener('mousemove', update))
onUnmounted(() => window.removeEventListener('mousemove', update))
// Expose the managed state through the return value
return { x, y }
}What does this function do?
- Creates two reactive variables
xandy. - Uses
onMountedto register amousemoveevent listener to updatexandy. - Uses
onUnmountedto clean up this listener when the component unmounts, preventing memory leaks. - Returns the object containing
xandy.
Using in Components
Now, any component can easily reuse this logic.
MouseTracker.vue
<script setup>
// Import the composable
import { useMouse } from '../composables/useMouse.js'
// Call it within the component scope
const { x, y } = useMouse()
</script>
<template>
Mouse position is at: {{ x }}, {{ y }}
</template>You can even use it multiple times in the same component, and their states won't interfere with each other.
Advantages
- Clear Logic: Related logic is encapsulated in the same function rather than scattered across different options.
- Clear Sources: When using in a component, we clearly know that
xandycome fromuseMouse, unlike Mixins where the source is unclear. - Code Reuse: Can easily reuse this stateful logic across any component.
- TypeScript Friendly: Regular functions and variables make type inference very natural.
Composables are the preferred way to organize and reuse logic in Vue 3 applications, making the codebase cleaner, more maintainable, and extensible.