Ваш первый композабл
Компоненты растут. В уроке «Жизненный цикл и данные» для загрузки меню понадобились три рефа и onMounted, и любому другому компоненту, который что-то загружает, пришлось бы повторять те же строки. Ответ Vue — композабл: функция, которая использует ref, computed, watch или хуки жизненного цикла Vue и возвращает то, что нужно компоненту. Её имя начинается с use, чтобы сразу было видно, что это композабл: useCounter, useLocalStorage, useFetch.
Вот счётчик в виде композабла. Он лежит в отдельном файле, useCounter.js:
import { ref } from "vue";
export function useCounter(start) {
const count = ref(start);
function increase() {
count.value++;
}
return { count, increase };
}А вот компонент, который его использует:
<script setup>
import { useCounter } from "./useCounter.js";
const { count, increase } = useCounter(0);
</script>
<template>
<p>Чашек кофе сегодня: {{ count }}</p>
<button @click="increase">+1 чашка</button>
</template>Что здесь нового:
- Композабл лежит в файле
.js, а не.vue: в нём нет ни шаблона, ни стилей, только JavaScript. Всё, что он использует, он импортирует из"vue". export functionэкспортирует функцию под её именем, какexport constв уроке «Компоненты». Поэтому компонент импортирует её в фигурных скобках, какrefиз"vue":import { useCounter } from "./useCounter.js";.- Компонент вызывает композабл в начале
<script setup>, как вызываетref(). Именно тогда композабл создаёт реф и функцию и возвращает их в объекте, а деструктуризация достаёт их оттуда:const { count, increase } = useCounter(0);. - С этого момента
countиincreaseработают так, будто их написали прямо в компоненте:count— это реф, который показывает{{ count }}, аincrease— функция для@click.
Каждый вызов создаёт новый реф: у двух компонентов, которые вызывают useCounter(), свой счётчик у каждого. Вызвать композабл — всё равно что скопировать его строки в компонент, только писать их приходится один раз.
Типичные ошибки:
import useCounter from "./useCounter.js";без фигурных скобок: так импортируется экспорт файла по умолчанию, которого у него нет. Страница остаётся пустой, а в консоли появляетсяTypeError.- Забытый
return: тогда функция возвращаетundefined, и деструктуризация заканчивается ошибкойTypeError, которая начинается со словCannot destructure property 'count'.
Задание
- 1
Вынесите счётчик в композабл. В
useCounter.jsнапишитеuseCounter(start), как показано выше. ВApp.vueимпортируйте его и замените реф и функцию наconst { count, increase } = useCounter(0);. Страница работает как прежде. - 2
Добавьте в композабл функцию
reset(), которая возвращаетcountк значениюstart, с которого он начинал, и верните её тоже. ВApp.vueдостаньте её вместе с остальными и добавьте после+1 чашкакнопкуСбросить, которая её вызывает.