Что такое реактивность во vue js

Подробно о реактивности

Мы уже разобрали большую часть основ, так что пришло время нырнуть поглубже! Одна из наиболее примечательных возможностей Vue — это ненавязчивая реактивность. Модели представляют собой простые JavaScript-объекты. По мере их изменения обновляется и представление данных, благодаря чему управление состоянием приложения становится простым и очевидным. Тем не менее, у механизма реактивности есть ряд особенностей, знакомство с которыми позволит избежать появление распространённых ошибок. В этом разделе руководства мы подробно рассмотрим низкоуровневую реализацию системы реактивности Vue.

Как отслеживаются изменения

Геттеры и сеттеры не видны пользователю, но именно они являются тем внутренним механизмом, который позволяет Vue отслеживать зависимости и изменения данных. К сожалению, при таком подходе выведенные в консоль браузера геттеры и сеттеры выглядят не так, как обычные объекты, поэтому для более наглядной визуализации лучше использовать инструменты разработчика Vue-devtools.

В каждый экземпляр компонента добавлен связанный с ним экземпляр наблюдателя, который помечает все поля, затронутые при отрисовке компонента, как зависимости. В дальнейшем, когда вызывается сеттер поля, помеченного как зависимость, этот сеттер уведомляет наблюдателя, который, в свою очередь, инициирует повторную отрисовку компонента.

Что такое реактивность во vue js. Смотреть фото Что такое реактивность во vue js. Смотреть картинку Что такое реактивность во vue js. Картинка про Что такое реактивность во vue js. Фото Что такое реактивность во vue js

Особенности отслеживания изменений

Вследствие ограничений JavaScript, есть виды изменений, которые Vue не может обнаружить. Однако существуют способы обойти их, чтобы сохранить реактивность.

Для объектов

Vue не может обнаружить добавление или удаление свойства. Так как Vue добавляет геттер/сеттер на этапе инициализации экземпляра, свойство должно присутствовать в объекте data для того чтобы Vue преобразовал его и сделал реактивным. Например:

Во Vue нельзя динамически добавлять новые корневые реактивные свойства в уже существующий экземпляр. Тем не менее, можно добавить реактивное свойство во вложенные объекты, используя метод Vue.set(object, propertyName, value) :

Для массивов

Vue не может отследить следующие изменения в массиве:

Для решения второй проблемы используйте splice :

Объявление реактивных свойств

Поскольку Vue не позволяет динамически добавлять корневые реактивные свойства, это означает, что все корневые поля необходимо инициализировать изначально, хотя бы пустыми значениями:

Если не задать поле message в опции data, Vue выведет предупреждение, что функция отрисовки пытается получить доступ к несуществующему свойству.

Существуют технические причины для этого ограничения: оно позволяет исключить целый класс граничных случаев в системе учёта зависимостей, а также упростить взаимодействие Vue с системами проверки типов. Но более важно то, что с этим ограничением становится проще поддерживать код, так как теперь объект data можно рассматривать как схему состояния компонента. Код, в котором реактивные свойства компонента перечислены заранее, намного проще для понимания.

Асинхронная очередь обновлений

Источник

Понимание реактивности во Vue.js (Шаг за Шагом)

В этой статье я собираюсь рассказать о реактивности во Vue. Начнем с определения, что такое реактивность.

Для меня реактивность означает, что когда изменилось состояние одного объекта, то все использующие его значения другие объекты то же автоматически обновились.

Если это попытаться объяснить в математическом определении, то у нас будет уравнение, в котором y = x + 1, x — наша независимая переменная, а y — наша зависимая переменная. Почему у называется зависимой переменной? Ну, потому что его значение зависит от значения х!

Хорошо известным примером системы реактивности является электронная таблица.

Шаг 1

Давайте забудем о фреймворках и шаг за шагом реализуем их в vanilla javascript. Для начала давайте сохраним нашу переменную x в объекте состояния (позже я объясню, почему она находится внутри объекта). Затем создадим функцию для отображения значения y в html. Наконец, вручную запустим функцию:

Шаг 2

Теперь давай те сделаем так что бы вместо ручного вызова функции renderY, у нас бы была бы функция setState, которая сама обновляла бы наше состояние и запускала бы функцию render.

В итоге мы получили React! 🙂

Но есть ли способ, которым мы можем вызвать функцию рендеринга автоматически, когда мы обновляем состояние?

Метод defineProperty для Object является функцией только для es5+. Вот почему vue 2 не поддерживает IE8 и ниже.

Это API позволяет нам переопределить поведение некоторых объектов по умолчанию, например, геттеров (getter) и сеттеров (setter).

Также именно поэтому мы хотим, чтобы состояние (state) было объектом. И это также объясняет, почему у vue есть некоторые махинации с массивами. Потому что это API отсутствует в массивах.

Давайте вызовем нашу функцию рендеринга в сеттере!

Мы берем значение x в начале, затем в геттере просто возвращаем его значение и в сеттере запускаем нашу функцию рендеринга после обновления значения x.

Это работает, но наш код выглядит так ужасно. Он работает только для свойства «x» и не работает для вложенного объекта. Давайте проведем рефакторинг нашего кода.

Шаг 3

Давайте создадим наблюдаемую функцию. Эта функция будет просто перебирать все ключи объекта и рекурсивно делает все вложенные объекты наблюдаемыми.

Из демонстрации, которую вы можете увидеть в этом особом случае, наш код все еще работает. Тем не менее, при обновленном состоянии наш код может выполнять только одну отдельную работу — рендеринг Y, и он всегда будет рендерить Y, независимо от того, какое состояние обновлено. Я считаю, что это ошибка. Поэтому мы должны найти способ заставить наше наблюдаемое состояние отслеживать, какой код на самом деле зависит от того, от какого состояния.

Шаг 4

Чтобы отслеживать зависимости кода, давайте создадим простой класс Dep, чтобы мы могли использовать его для создания экземпляра dep для каждого ключа объекта.

Как вы можете видеть, этот класс довольно прост: в конструкторе мы просто создаем набор заданий jobs, причина использования Set в том, что мы не хотим добавлять дубликаты заданий для одного и того же ключа.

Метод depend просто добавляет текущее задание job в набор заданий jobs а метод notify запускает все добавленные задания.

Обратите внимание, что у нас также есть статическая переменная job для хранения текущего оцениваемого задания.

Теперь давайте используем класс Dep для обновления нашей наблюдаемой функции.

Для каждого ключа мы создаем новый экземпляр класса Dep для отслеживания зависимой job.

Но как мы узнаем, зависит ли текущее задание от этого свойства объекта? Правильно, нам просто нужно запустить это задание один раз (что мы уже делали в предыдущих демонстрациях), когда задание запускается, если задание пытается получить доступ к этому свойству объекта, мы знаем, что это задание зависит от этого свойства. Поэтому мы можем вызвать dep.depend(), чтобы добавить текущее задание к экземпляру Dep внутри геттера.

И затем, когда это свойство обновляется, мы хотим перезапустить все сохраненные задания, вызвав dep.notify() внутри сеттера.

Это может немного смущать вас, но давайте просто соберем все вместе в демоверсии, чтобы вы могли видеть, как все происходит более четко.

Таким образом, как вы можете видеть, при запуске задания renderY функция пытается получить state.x, поэтому будет запущен метод геттер для x, поэтому задание renderY будет добавлено в job, установленные для x.

Шаг 5

В нашем демо у нас есть только одна работа, renderY. Теперь я хочу получить другую job для renderX, но я не хочу копировать и вставлять код для установки текущей job; потом снова запусткать job, а затем очищать job.

Опять же, как хороший разработчик, мы всегда должны помнить о принципе DRY.

Итак, мы можем создать простую функцию раннера, которая сделает всю эту грязную работу за нас.

Давайте вернемся к нашему codepen и добавим больше раннеров;

К настоящему времени мы только что внедрили очень базовую версию системы реактивности vue. Конечно, настоящий код vue гораздо сложнее, чем этот. Потому что нужно рассмотреть множество исключений и крайних случаев, а код должен быть более организованным.

Но если вы захотите проверить исходный код vue, вы увидите класс Dep, функцию, которая делает объект реактивным, и класс наблюдателя (watcher), выполняющий в основном ту же работу, что и наша простая функция runner.

В целом, эта система, основанная на API object.defineProperty, работает нормально в большинстве случаев. Но каковы особенности этой системы? Помните, я говорил, что API object.defineProperty не работает с массивом? Vue сделал переопределение некоторых методов-прототипов, таких как array.push, чтобы сделать массив реактивным. Другое дело, что таким образом мы только активируем начальные свойства объекта, а как насчет новых свойств, которые вы добавите к объекту позже? Vue не может знать о новом свойстве заранее, поэтому оно не может сделать его реактивным. Поэтому каждый раз, когда вы хотите добавить новое реактивное свойство к объекту данных, вы должны использовать Vue.set.

Тем не менее, всех этих махинаций больше не будут в Vue 3.0. Потому что система реактивности Vue 3.0 будет полностью переписана с помощью Object Proxy. В настоящее время Vue 3.0 все еще находится в активной стадии разработки, и мы пока не не можем увидить исходный код. Однако Evan You, создатель Vue, объяснил, как это будет работать на семинаре, который я посетил на конференции Vue в Торонто несколько месяцев назад. Сейчас я покажу вам, ребята, что я узнал из этого.

Еще кое-что…

Proxy — это новый глобальный API, представленный в ES6. Это функция уровня языка, которая означает, что это не полифиллинг, а также означает, что Vue 3.0 по умолчанию не поддерживает IE11 и ниже. Однако вы можете настроить свою сборку для использования старой системы реактивности на основе object.defineProperty.

Если вы ранее не слышали о Proxy API, вы можете прочитать какой-нибудь документ об этом на одном MDN или в вашем любимом w3school.

Прежде всего, давайте определим наши traps. В геттере trap мы создаем deps, если он еще не создан в хранилище, далее мы вызываем dep.depend(), чтобы зарегистрировать текущее задание как зависимость, как мы это делали раньше. То же самое в сеттере trap, мы обновляем значение в target, а затем вызываем dep.notify() для запуска job.

Затем мы обновляем нашу наблюдаемую функцию, чтобы использовать Proxy с нашими обработчиками. Мы создаем WeakMap для хранения наблюдаемого объекта, поэтому мы не будем наблюдать один и тот же объект несколько раз.

Заключение

В этой статье я рассказал как устроена реактивность в Vue 2 и как будет устроена в будущей версии Vue 3. Если у вас есть вопросы или свое мнение на этот счет добро пожаловать, в комментарий автора статьи.

Источник

Изучаем реактивность во Vue.js. Простое объяснение

В этой статье я попытаюсь:

Начнем рассмотрение с самого простого и типичного приложения Vue.js

Соответствующий шаблон будет таким:

Как мы можем видеть переменные объявленные в коде просто отображаются в шаблоне. При этом при изменение значения переменных в коде они автоматически будут обновляется в шаблоне. Такой механизм и называется реактивность Vue.js. Возникает резонный вопрос как Vue.js понимает что значение переменной изменилось и его нужно обновить в шаблоне.

В этой статье мы построим систему реактивности с нуля что бы понять как это работает во Vue.js

Начнем с рассмотрения простого скрипта JavaScript:

Возникает вопрос как нам сохранить вычисление total таким образом что бы мы могли снова его запустить когда значения price или quantity изменяться. Нам надо каким то образом сохранить код
let total = price * quantity в хранилище, а затем запускать его при необходимости. Для этого добавим функцию target

Далее добавим механизм сохранения функции вычисления:

И там мы рассмотрели общий принцип идеи реализации реактивности на самом простом примере.

Теперь давайте сделаем наше решение более масштабируемым, создадим класс для сохранения наших зависимостей. Мы сделаем наш класс на основе шаблона Observer

Кстати во Vue.js то же есть класс Dep который по своему предназначению напоминает созданный нами класс.

Сейчас мы отслеживаем изменения значения только у конкретной выбранной переменной.
Далее нам нужно будет сделать так что бы у каждой отслеживаемой переменной был экземпляр Dep класса. Для этого нам нужно как то соединить все переменные которые мы хотим отслеживать и класс Dep.

Код который мы использование ранее

Вместо него нам нужно функция watcher

Более универсальная функция:

Вернемся к нашему примеру и добавим в него функцию watcher

но может быть другой watcher

Так же нам необходимо каким то образом определять что внутри wacher функции что бы подключать Dep класс. Таким образом что бы при вызове первого watcher dep.depend() вызывался при обновление переменной price и quantity а во втором watcher dep.depend() вызывался только при обновление price.

Для этого нам нужен способ благодаря которому мы можем понять что значение переменной было изменено, и тогда мы можем вызвать в этот момент функцию обновления dep.notify()

Для этого во Vue.js 2.X используется Object.defineProperty()

Обратите внимание что Object.defineProperty() используется только в в первой и второй версии Vue.js В третьей версии для этих целей используется другой механизм основанный на Proxy.

Функция Object.defineProperty() — позволяет определять геттер и сеттер функции на любые свойства объекта

Обратите внимание что данный пример в реальности ничего не меняет, что бы это происходило нам нужно реализовать этот функционал:

Далее нам нужно вызывать defineProperty для каждого значения в data

Теперь объединим рассмотренные выше две идеи watcher плюс класс Dep и функции defineProperty

Теперь взглянем на то как наш пример должен выглядеть целиком

Теперь подумаем как это может быть реализовано во Vue.js. Рассмотрим классическую картинку из документации Vue показывающее устройство реактивности во Vue

Здесь у нас есть Data, Геттеры/Сеттеры, Watcher. Объект Data для своей работы использует Object.defineProperty. Когда геттер задействуется запускается метод dep.depend() класса Dep. А когда сеттер задействуется вызывается метод dep.notify() так же класса Dep. Тут так же есть Watcher устройство которого более подробно мы рассмотрим в другой моей статье

Реализации реактивности во Vue.js 3.0

Рассмотрим с помощью нашего примера реализацию реактивности в 3 версии Vue.js. Как уже было сказано оно реализуется через объект Proxy. Этот объект оборачивается вокруг другое объекта и позволяет перехватывать все действия в ним.

Используем Proxy в нашем примере:

Теперь мы можем добавить новое реактивное свойство:

Внимание вопрос: Зачем понадобилось в 3 версии Vue.js переходить от defineProperty к Proxy?

На самом деле defineProperty не позволяет создавать полноценные прозрачные реактивные системы. При определенных условиях например в коллекциях измененные значения не обнаруживаются реактивной системой и поэтому значения внутри массива нужно менять используя метод $set. Объект Proxy не имеет таких недостатков.

Заключение

В этом уроке я попытался рассказать о том как:

Источник

Реактивность Vue.js

Реактивность фреймворка Vue.js и использование метода Vue.set

Что такое реактивность во vue js. Смотреть фото Что такое реактивность во vue js. Смотреть картинку Что такое реактивность во vue js. Картинка про Что такое реактивность во vue js. Фото Что такое реактивность во vue js

Что такое реактивность во vue js. Смотреть фото Что такое реактивность во vue js. Смотреть картинку Что такое реактивность во vue js. Картинка про Что такое реактивность во vue js. Фото Что такое реактивность во vue js

В статье рассматривается система реактивности Vue.js и приводится пример того, когда эта система не работает.

Также даются два способа решения этой проблемы, один из которых — использование встроенного метода Vue.set.

Одна из возможностей, за которую мы любим Vue.js— это система реактивности. Для наглядного примера такой реактивности Vue.js можно привести нижеследующий код.

В этом примере первоначально в шаблоне генерируется вывод текста “Hello World”.

Затем по истечении 2 секунд текст в шаблоне меняется на “Goodbye World”:

В приведенном выше примере ключ message объекта data является реактивным.

Это означает, что при любом изменении значения ключа message происходит автоматическое изменение выводимого значения в шаблоне.

Ловушка системы реактивности

Такая автоматизация предоставляет ряд несомненных преимуществ при создании приложений на Vue.js:

Другими словами — делает жизнь разработчика более простой.

Однако, такая простота имеет свою обратную сторону медали. Подвох в данном случае подобен аналогии автомобилю с автопилотом.

Автоматическая реактивность делает нас, как разработчиков, ленивыми. И когда что-то работает не так, как мы бы этого ожидали — мы не знаем, в чем причина того, что так произошло.

Когда реактивность оборачивается злом

Один из студентов курса Ultimate Vue.js Developers столкнулся с интересной проблемой и поделился ею со мной. Этот студент работает над проектом Vue.js Poster Shop — первый учебный проект данного курса.

В этом проекте есть задача — создание объектов shopping cart при помощи Vue.js. Такой объект cart отображается в действующем проекте как обычный JavaScript-объект:

Логика этого метода следующая:

Появляется проблема

Коллекция объектов cart представляет из себя простой список объектов в шаблоне:

Проблема заключается в том, что в шаблоне значение ключа quantity всегда будет выводиться как 1.

Другими словами, вне зависимости от того, изменилось ли значение ключа quantity или просто этому ключу было присвоено значение 1 — в любом случае в шаблоне будет выведено значение 1.

Моей первой мыслью было, что логика метода addToCard имеет в себе ошибку.

Так что проблема была не здесь.

Как реактивность работает “под капотом”

Как уже упоминалось ранее, побочным следствием автоматической работы системы реактивности Vue.js является тот факт, что если код работает не так, как было задумано, разработчику трудно понять, что является причиной такого поведения.

Однако, такой ситуации можно избежать, если понимать, как в действительности работает система реактивности Vue.js.

Getters и setters

Стандартное поведение JavaScript-объекта, когда производится извлечение значения или изменение значения какого-либо ключа этого объекта — вопрос однозначный.

К примеру, имеется такой JavaScript-объект:

Извлечение значения ключа a объекта myObj можно выполнить таким образом:

Для более подробной информации об обоих псевдо-свойствах можно перейти по ссылкам — getter и setter.

Такой набор getter’ов и setter’ов позволяет Vue отслеживать любое изменение в любом свойстве и автоматически отображать эти изменения в шаблоне.

Метод reactiveSetter()

Вернемся назад, к JavaScript-объекту myProduct :

После того, как экземпляр Vue был инициализирован, можно открыть консоль браузера и увидеть, что для каждого ключа этого объекта Vue.js создал пару getter/setter:

Что такое реактивность во vue js. Смотреть фото Что такое реактивность во vue js. Смотреть картинку Что такое реактивность во vue js. Картинка про Что такое реактивность во vue js. Фото Что такое реактивность во vue js

Эти функции getter и setter выполняют большой объем работы (посмотрите исходный код).

Подробнее

Vue.js является отличным фреймворком, за исключением одного случая.

Если в существующий объект добавить или удалить свойство после того, как этот объект был инициализирован системой Vue.js, то Vue ничего не будет знать об этом свойстве.

Например, добавим в JavaScript-объект myProduct новое свойство quantity после того, как Vue.js выполнил инициализацию этого объекта:

Обновление реактивных объектов

В примере с объектом myProduct проблему обновления реактивного объекта можно решить двумя способами.

Первый способ — это создать новый объект и затем добавить его в коллекцию уже существующих объектов.

Такой подход дает гарантию того, что у Vue.js будет возможность создать пару getter/setter для каждого свойства вновь создаваемого объекта.

Метод Vue.set

Данный метод гарантирует, что для вновь создаваемого свойства в уже существующем реактивном объекте будут создана пара getter/setter и данное свойство также станет реактивным.

Метод Vue.set добавит в объект item новое свойство quantity и автоматически создаст для этого свойства пару getter/setter, тем самым сделав свойство quantity реактивным:

Массивы

Подобно объектам, массивы также являются реактивными в системе Vue.js и благодаря этому могут отслеживать факт наличия изменений.

Также как и с объектами, в массив нельзя добавить новый элемент “напрямую” и ожидать реактивности от нового элемента.

Например, добавление в массив нового элемента по индексу не даст желаемого результата:

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *