Vue
VueUse: экосистема composables
Каждый проект рано или поздно пишет одно и то же: синхронизацию настроек с localStorage, дебаунс поля поиска, отслеживание видимости блока для ленивой подгрузки, обёртку над fetch. Это сотни строк, которые надо покрыть тестами и поддерживать с учётом краевых случаев: реактивная запись в storage, очистка таймера дебаунса при размонтировании, отписка от IntersectionObserver. VueUse это библиотека из двух с лишним сотен таких composable, обкатанных сообществом, где краевые случаи уже учтены.
- Настройки темы: useLocalStorage синхронизирует ref с localStorage реактивно и переживает перезагрузку
- Поиск: useDebounceFn откладывает запрос, пока пользователь печатает, без ручного setTimeout
- Лента: useElementVisibility подгружает следующую страницу, когда блок-якорь попал в вьюпорт
- Дашборд: useFetch даёт data, error и isFetching без написания собственного композабла
- Конструктор: useMouse отслеживает координаты курсора для перетаскивания элементов
Предварительные знания
- Написание собственных composable: функция use*, возвращающая реактивное состояние
- Хуки жизненного цикла и очистка ресурсов при размонтировании
- ref и computed, понимание реактивной связи значения
Зачем готовые composable
Типовые задачи (storage, дебаунс, отслеживание видимости, координаты мыши) выглядят простыми, но содержат краевые случаи: очистка таймеров и подписок при размонтировании, реактивная синхронизация, обработка SSR-окружения без window. Самописная версия часто игнорирует часть из них и течёт. VueUse даёт обкатанные реализации, где эти случаи уже учтены.
| Задача | Самописно | VueUse |
|---|---|---|
| Синхронизация с localStorage | Ручная запись + чтение | useLocalStorage |
| Дебаунс функции | setTimeout + очистка | useDebounceFn |
| Видимость элемента | IntersectionObserver + отписка | useElementVisibility |
| Координаты курсора | addEventListener + removeEventListener | useMouse |
| Загрузка данных | fetch + loading/error | useFetch |
VueUse это древовидно-встряхиваемая (tree-shakeable) библиотека: в бандл попадают только импортированные функции, а не весь набор. Поэтому использование пары composable не утяжеляет приложение на все 200+.
Прежде чем писать очередной use* для типовой задачи, стоит проверить, нет ли его в VueUse. Готовая версия уже покрыта тестами и учитывает очистку ресурсов, на которую в спешке легко забить.
Какое главное преимущество готовых composable VueUse перед самописными для типовых задач?
useLocalStorage и useFetch
useLocalStorage возвращает ref, связанный с ключом localStorage. Чтение даёт сохранённое значение или дефолт, запись в ref автоматически пишет в storage, а значение переживает перезагрузку. Сериализация и парсинг JSON происходят внутри composable.
useFetch это готовая обёртка над fetch, отдающая реактивные data, error и isFetching, а также методы для повтора и отмены. Это та же связка состояний, что писали вручную в уроке про загрузку данных, но без собственного кода.
useLocalStorage синхронизирует значение и между вкладками через событие storage: смена темы в одной вкладке отразится в другой. Самописная версия такое поведение обычно упускает.
Что происходит при записи в ref, возвращённый из useLocalStorage?
useDebounceFn и useElementVisibility
useDebounceFn оборачивает функцию так, что та вызывается не чаще раза в заданный интервал после последнего обращения. Это убирает шквал запросов при наборе текста. Внутренний таймер очищается при размонтировании автоматически, чего легко забыть в самописной версии.
useElementVisibility оборачивает IntersectionObserver и отдаёт реактивный булев флаг видимости элемента во вьюпорте. Это база для ленивой подгрузки: когда блок-якорь становится видимым, грузят следующую порцию. Отписка от observer происходит при размонтировании сама.
Самописный дебаунс без очистки таймера в onUnmounted и observer без отписки оставляют висящие колбэки на размонтированном компоненте. Это утечки и ошибки обращения к мёртвому состоянию, которые VueUse закрывает автоматически.
Что useDebounceFn делает за разработчика помимо самой задержки вызова?
Когда писать своё, а когда брать готовое
VueUse не отменяет навык писать composable. Граница такая: типовая, переиспользуемая многими проектами задача (storage, дебаунс, видимость, мышь, медиазапросы) это VueUse. Специфичная бизнес-логика приложения (загрузка именно заказов с именно вашими правилами) это собственный composable, возможно поверх примитивов VueUse.
- Берут VueUse — Задача общая и встречается в любом проекте. Краевые случаи уже решены и протестированы сообществом. Свой код дублировал бы это
- Пишут своё — Логика специфична для домена приложения. Готового аналога нет или он не покрывает нужное поведение. Часто строят поверх примитивов VueUse
useMouse и useElementVisibility часто становятся кирпичами для собственных composable: например, useDraggable внутри опирается на координаты курсора. Так VueUse служит и готовым решением, и набором примитивов для своих абстракций.
Полезное правило: общую инфраструктурную задачу не пишут заново, а доменную не тащат в общую библиотеку. VueUse закрывает первое, собственный composable второе.
Какую задачу логичнее решать собственным composable, а не функцией из VueUse?
Связь с другими темами
Урок про готовые composable. Они закрывают задачи из соседних тем курса:
- Загрузка данных — useFetch инкапсулирует тот же паттерн loading/error, что писали вручную
- Валидация форм — Та же идея переиспользования: готовое решение вместо самописного кода
Итог
- VueUse это библиотека из 200+ готовых composable, где учтены реактивность, очистка ресурсов и краевые случаи
- useLocalStorage возвращает ref, синхронизированный с localStorage: запись в ref пишет в storage и переживает перезагрузку
- useFetch даёт data, error и isFetching из коробки, повторяя ручной паттерн загрузки данных
- useDebounceFn и useElementVisibility оборачивают таймеры и IntersectionObserver с автоматической очисткой при размонтировании
- Самописный composable оправдан для специфичной логики; типовые задачи закрывает проверенный VueUse
Связанные уроки
- vue-30-data-fetching — useFetch из VueUse это готовая реализация паттерна loading/error/refetch из урока про загрузку данных
- vue-32-form-validation — Как VeeValidate для валидации, VueUse даёт готовые composable вместо самописных решений