Компоновані у Vue.js
Що таке Composables?
Composables — це функції, які використовують Composition API для інкапсуляції та повторного використання стану між компонентми.
Composables у Vue схожі на кастомні хуки у React.
Основний приклад
// useCounter.js
import { ref } from 'vue'
export function useCounter(initialValue = 0) {
const count = ref(initialValue)
function increment() {
count.value++
}
function decrement() {
count.value--
}
function reset() {
count.value = initialValue
}
return {
count,
increment,
decrement,
reset
}
}Використання:
<template>
<div>
<p>Count: {{ count }}</p>
<button @click="increment">+</button>
<button @click="decrement">-</button>
<button @click="reset">Reset</button>
</div>
</template>
<script setup>
import { useCounter } from './useCounter'
const { count, increment, decrement, reset } = useCounter(10)
</script>Конвенції іменування
Composables зазвичай називаються з префіксом use:
// Добре
useCounter()
useMouse()
useFetch()
useLocalStorage()
// Погано
counter()
mouse()
fetch()Популярні приклади Composables
useMouse
Відстеження позиції миші:
// useMouse.js
import { ref, onMounted, onUnmounted } from 'vue'
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 }
}useFetch
Отримання даних з API:
// useFetch.js
import { ref } from 'vue'
export function useFetch(url) {
const data = ref(null)
const error = ref(null)
const loading = ref(false)
async function fetch() {
loading.value = true
error.value = null
try {
const response = await window.fetch(url)
data.value = await response.json()
} catch (e) {
error.value = e
} finally {
loading.value = false
}
}
fetch()
return { data, error, loading, refetch: fetch }
}useLocalStorage
Синхронізація з localStorage:
// useLocalStorage.js
import { ref, watch } from 'vue'
export function useLocalStorage(key, defaultValue) {
const value = ref(defaultValue)
// Читання з localStorage при ініціалізації
const stored = localStorage.getItem(key)
if (stored) {
try {
value.value = JSON.parse(stored)
} catch (e) {
console.error('Error parsing localStorage value:', e)
}
}
// Збереження при зміні
watch(value, (newValue) => {
localStorage.setItem(key, JSON.stringify(newValue))
}, { deep: true })
return value
}Комбінування Composables
Composables можуть бути комбіновані:
// useUser.js
import { useFetch } from './useFetch'
import { useLocalStorage } from './useLocalStorage'
export function useUser() {
const userId = useLocalStorage('userId', null)
const url = computed(() =>
userId.value ? `/api/users/${userId.value}` : null
)
const { data: user, loading, error } = useFetch(url)
function login(id) {
userId.value = id
}
function logout() {
userId.value = null
}
return {
user,
loading,
error,
login,
logout
}
}Найкращі практики
Повернення refs, а не reactive
// Добре
export function useCounter() {
const count = ref(0)
return { count }
}
// Погано
export function useCounter() {
const state = reactive({ count: 0 })
return state
}Причина: refs зберігають реактивність при деструктуризації.
Очищення в onUnmounted
export function useEventListener(target, event, callback) {
onMounted(() => {
target.addEventListener(event, callback)
})
onUnmounted(() => {
target.removeEventListener(event, callback)
})
}Використовуйте toValue для гнучкості
import { toValue } from 'vue'
export function useFetch(url) {
// url може бути ref, computed або простим значенням
const actualUrl = toValue(url)
// ...
}Composables проти Mixins
Mixins (Vue 2)
// Погано - mixins
const counterMixin = {
data() {
return {
count: 0
}
},
methods: {
increment() {
this.count++
}
}
}Проблеми з Mixins:
- Невизначене джерело властивостей
- Конфлікти імен
- Непрямі залежності
Composables (Vue 3)
// Добре - composables
export function useCounter() {
const count = ref(0)
function increment() {
count.value++
}
return { count, increment }
}Переваги Composables:
- Ясне джерело
- Відсутність конфліктів імен
- Краща типізація
- Легше тестувати
Загальні помилки
Виклик composable поза setup
// Неправильно
export default {
data() {
const { x, y } = useMouse() // Помилка!
return { x, y }
}
}
// Правильно
export default {
setup() {
const { x, y } = useMouse()
return { x, y }
}
}Умовний виклик composable
// Неправильно
if (condition) {
const { count } = useCounter() // Помилка!
}
// Правильно
const { count } = useCounter()
if (condition) {
// використати count
}Забування про очищення
// Неправильно - витік пам'яті
export function useMouse() {
const x = ref(0)
function update(event) {
x.value = event.pageX
}
window.addEventListener('mousemove', update)
// Забули removeEventListener!
return { x }
}
// Правильно
export function useMouse() {
const x = ref(0)
function update(event) {
x.value = event.pageX
}
onMounted(() => {
window.addEventListener('mousemove', update)
})
onUnmounted(() => {
window.removeEventListener('mousemove', update)
})
return { x }
}Висновок
Composables:
- Повторювана логіка з Composition API
- Краще, ніж mixins
- Називаються з префіксом
use - Повертають refs
- Потребують очищення в onUnmounted
- Можуть бути комбіновані
- Підтримують TypeScript
На співбесідах:
Важливо вміти:
- Пояснити, що таке composables і чому вони потрібні
- Показати приклади створення composables
- Описати переваги над mixins
- Пояснити найкращі практики (іменування, очищення, повернення refs)
- Навести приклади популярних composables
Зміст
Що таке Composables?Основний прикладКонвенції іменуванняПопулярні приклади ComposablesuseMouseuseFetchuseLocalStorageКомбінування ComposablesНайкращі практикиПовернення refs, а не reactiveОчищення в onUnmountedВикористовуйте toValue для гнучкостіComposables проти MixinsMixins (Vue 2)Composables (Vue 3)Загальні помилкиВиклик composable поза setupУмовний виклик composableЗабування про очищенняВисновок
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.