Привет! Меня зовут Алексей, я frontend-специалист SimbirSoft. В этой статье разберем новый метод слежения за реактивными свойствами watchEffect.
С появлением Vue 3 c Composition API стало доступно два метода слежения — watch и watchEffect. Если «старый» метод watch всем хорошо знаком и не должен вызывать затруднений у Vue-разработчиков, то новый метод стоит изучить подробнее. Материал будет полезен разработчикам, переходящим с Vue 2 на Vue 3 и всем «вьюшникам», которые еще не разобрались с этим методом.

Composition API предоставляет нам два разных метода слежения за реактивными свойствами — watch и watchEffect. Они похожи, но все же каждый полезен в определенных случаях. Рассмотрим, какие сходства и различия существуют у этих методов:
|
Общее |
Отличия |
|
●следят за изменениями зависимостей |
●watch (без immediate) может использоваться для ленивого запуска побочных эффектов (watchEffect всегда отрабатывает немедленно после монтажа компонента) |
Это были основные отличия, о других мы расскажем ниже.
Слежение за объектами и массивами в watchEffect
WatchEffect может отслеживать только адрес памяти реактивного объекта. Изменение элементов массива или свойств объекта не изменит адрес памяти и, следовательно, не вызовет срабатывания метода watchEffect:
<template> <div class="home"> <button @click="addTarget">add</button> <div>{{ target }}</div> </div> </template> <script setup> import { reactive, watchEffect } from "vue"; const target = reactive([1, 2, 3, 4]); watchEffect(() => { console.log(target); }); const addTarget = () => { target.push(5); }; </script>
При нажатии на кнопку add видим, что данные в массив добавляются, но watchEffect отрабатывает только один раз. WatchEffect всегда срабатывает только один раз после монтажа компонента, в watch нам приходилось указывать для этого immediate — true:
Нам нужно преобразовать реактивный объект обратно в массив с помощью spread-оператора:
<template> <div class="home"> <button @click="addTarget">add</button> {{ target }} </div> </template> <script setup> import { reactive, watchEffect } from "vue"; const target = reactive([1, 2, 3, 4]); watchEffect(() => { const arr = [...target]; console.log(arr); }); const addTarget = () => { target.push(5);}; </script>
Теперь наш watchEffect успешно отрабатывает:
Для слежения за объектами будем использовать toRefs():
<template> <div class="home"> <button @click="setTitle">changeTitle</button> {{ data }} </div> </template> <script setup> import { reactive, watchEffect, toRefs } from "vue"; const data = reactive({ title: "Some title", desc: "Some description", }); watchEffect(() => { const parse = toRefs(data); console.log(parse); }); const setTitle = () => { data.title = "New title"; }; </script>
Вызов функции stop() остановит действие watchEffect:
<template> <div class="home"> <button @click="setTitle">changeTitle</button> <button @click="stop">stop</button> <div> {{ data }} </div> </div> </template> <script setup> import { reactive, watchEffect, toRefs } from "vue"; const data = reactive({ title: "Some title", desc: "Some description", }); const stopWatchEffect = watchEffect(() => { const parse = toRefs(data); console.log(parse); }); const setTitle = () => { data.title = "New title"; }; const stop = () => { stopWatchEffect(); }; </script>
Параметры watchEffect
Метод watchEffect принимает два аргумента. Первый — это коллбэк-функция. Второй — объект конфигурации:
watchEffect( () => {}, { flush: 'post', onTrack(e) { debugger }, onTrigger(e) { debugger } })
Свойство flush определяет, запускается ли метод watchEffect до, после или во время повторного рендеринга страницы:
flush: 'pre' | 'post' | 'sync'
По умолчанию созданные пользователем коллбек-функции наблюдателя вызываются до обновления компонентов Vue. Это означает, что если вы попытаетесь получить доступ к DOM внутри коллбек-функции наблюдателя, DOM будет в состоянии до того, как Vue применит какие-либо обновления.
Дополнительный объект настроек с опцией flush (значение по умолчанию — ‘pre’):
let stop = watchEffect(callback, { flush: 'pre' })
Опция flush также может принимать значение ‘sync’, которое принудительно заставит эффект всегда срабатывать синхронно. Однако такое поведение неэффективно и должно использоваться только в крайних случаях:
watchEffect(callback, { flush: 'sync' })
Если вы хотите получить доступ к DOM в коллбек-функции наблюдателя после того, как Vue его обновил его, вам нужно указать flush: ‘post’:
watchEffect(callback, { flush: 'post' })
Отладка watchEffect
Можно использовать опции onTrack и onTrigger для отладки поведения наблюдателя:
-
onTrack вызывается, когда реактивное свойство или ссылка начинает отслеживаться как зависимость;
-
onTrigger вызывается, когда коллбэк наблюдателя вызван изменением зависимости.
Оба коллбэка получают событие отладчика с информацией о зависимости, о которой идет речь. Обратите внимание, опции onTrack и onTrigger работают только в режиме разработки.
Аннулирование побочных эффектов
Иногда в функции наблюдателя могут быть асинхронные побочные эффекты, которые требуют дополнительных действий при их аннулировании (то есть в случаях, когда состояние изменилось до того, как эффекты завершились). Для таких случаев функция эффекта принимает функцию onInvalidate. Она будет использоваться для аннулирования выполненного и вызываться в следующих случаях:
-
когда эффект будет вскоре запущен повторно;
-
когда наблюдатель остановлен (то есть когда компонент размонтирован, если watchEffect используется внутри setup() или хука жизненного цикла).
<template> <div class="home"> <button @click="setTitle">changeTitle</button> <button @click="stop">stop</button> {{ data }} </div> </template> <script setup> import { reactive, watchEffect, toRefs } from "vue"; const data = reactive({ title: "Some title", desc: "Some description", }); const stopWatchEffect = watchEffect((onInvalidate) => { const parse = toRefs(data); console.log(parse); console.log("basic function"); onInvalidate(() => { console.log("onInvalidate function"); }); }); const setTitle = () => { data.title = "New title"; }; const stop = () => { stopWatchEffect(); }; </script>
Снова запустим наше приложение:
Видим, что сначала отработала основная коллбек-функция, и в консоли вывелось basic function.
Если мы нажмем на кнопку stop, то сработает функция onInvalidate:
Если нажать на кнопку change title, то вначале выполнится функция onInvalidate, а затем основная функция. При изменении свойств onInvalidate будет всегда срабатывать первой:
Практическое применение watchEffect
Мы будем отменять запросы axios, если входные данные изменились, и старые данные больше не актуальны:
<template> <div class="home"> <div> <button @click="setType('albums')">load albums</button> <button @click="setType('posts')">load post</button> <button @click="stop">stop</button> </div> <div> {{ information }} </div> </div> </template> <script setup> import { ref, watchEffect } from "vue"; import axios from "axios"; const type = ref("albums"); const information = ref(null); const source = ref(null); const setType = (value) => { type.value = value; }; const init = async function () { source.value = axios.CancelToken.source(); try { const data = await axios.get( `https://jsonplaceholder.typicode.com/${type.value}`, { cancelToken: source.value.token, } ); return data; } catch (error) { console.log(error); } }; const stop = watchEffect((onInvalidate) => { onInvalidate(() => { source.value.cancel("Отмена запроса"); }); init(type.value).then((res) => { information.value = res; }); }); </script>
Функция setType изменит значение переменной type в зависимости от параметра, который был принят. WatchEffect следит за изменением переменной type, и при ее изменении делает запрос на получение постов или альбомов. Заметьте, мы нигде изначально не инициализируем запрос, поскольку watchEffect срабатывает один раз сразу при загрузке страницы:

Установим скорость сети в Slow 3G. И будем быстро переключаться по кнопкам load post и load alboms. Мы увидим, что запросы, которые больше не актуальны, отменяются.
Резюме
Мы разобрались с методом наблюдения за реактивными сущностями с помощью watchEffect.
Что следует запомнить о методе watchEffect:
-
он всегда срабатывает один раз после монтажа компонента;
-
он может отслеживать несколько переменных;
-
если вы хотите отслеживать изменения после обновления компонента, используйте опцию flush: ‘post’;
-
функция onInvalidate всегда срабатывает перед основной функцией;
-
для слежения за массивами используйте спред-оператор, а для слежения за объектами — функцию toRefs().
Подписывайся на наши соцсети! Авторские материалы для frontend-разработчиков мы также публикуем в ВК и Telegram.
ссылка на оригинал статьи https://habr.com/ru/company/simbirsoft/blog/697910/
Добавить комментарий