Как Vue.js реагирует на данные?

внешний интерфейс JavaScript Vue.js
Как Vue.js реагирует на данные?

Многие интерфейсные JavaScript-фреймворки, такие как Angular, React и Vue, имеют собственныеданные, соответствующиедвигатель. Поняв отзывчивость и то, как она работает, вы сможете улучшить свои навыки разработки и более эффективно использовать фреймворки JavaScript. В видео и статье ниже мы создаем тот же тип реактивности, который вы видите в исходном коде Vue.

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

💡 Система реактивности

Когда вы впервые видите это, Vueсистема реагированияЭто выглядит потрясающе. Возьмите это простое приложение Vue:

Каким-то образом Vue знает только то, что если цена изменится, он должен сделать три вещи:

  • Обновите значения цен на наших веб-страницах.
  • Пересчитайте выражение, умноженное на цену * количество, и обновите страницу.
  • Снова вызовите функцию totalPriceWithTax и обновите страницу.

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

Это не то, как обычно работает программирование на JavaScript.

Если вы не понимаете, давайте попробуем посмотреть, как работает обычный JavaScript. Например, если я запускаю этот код:

Как вы думаете, что он печатает? Поскольку мы не используем Vue, он напечатает 10.

В Vue мы хотим, чтобы общая сумма обновлялась всякий раз, когда обновляется цена или количество. Мы хотим:

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

⚠️ Вопросы

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

✅ Решения

Во-первых, нам нужен какой-то способ сообщить нашему приложению: «Я собираюсь запустить код, сохранить его, мне может понадобиться, чтобы вы запустили его в другое время». переменная цены или количества обновляется, снова запустите сохраненный код.

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

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

Записанный метод:

Мы сохраняем цель (в нашем случае {общая = цена * количество}), чтобы мы могли запустить ее позже.

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

Затем в нашем коде мы можем:

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

⚠️ Вопросы

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

✅ Решение: использовать класс

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

Итак, если мы создадим класс JavaScript для управления нашими зависимостями (что ближе к тому, как Vue обрабатывает вещи), он может выглядеть так:

заставить его работать:

Он по-прежнему работает, и теперь наш код кажется более надежным. Единственное, что все еще кажется немного странным, это настроенная и работающая target().

⚠️ Вопросы

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

(это просто код выше)

Мы можем изменить на:

✅ Решение: функция наблюдателя

В нашей функции Watcher мы можем сделать что-то простое:

Как видите, функция-наблюдатель принимает параметр myFunc, устанавливает его в значение нашего глобального целевого свойства, вызывает dep.depend() для добавления цели в качестве подписчика, вызывает целевую функцию и сбрасывает цель.

Теперь, когда мы запускаем следующее:

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

⚠️ Вопросы

У нас есть класс Dep, но на самом деле мы хотим, чтобы каждая переменная имела свой собственный Dep. Прежде чем идти дальше, давайте сохраним данные.

Предположим, что каждый из наших атрибутов (цена и количество) имеет собственный внутренний класс Dep.

Когда мы запускаем:

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

Если у меня есть другая анонимная функция, которая просто обращается к data.price, я хочу передать только класс Dep свойства цены.

Когда я хочу вызвать dep.notify() для подписчика цены? Я хочу, чтобы им позвонили, когда цена будет установлена. В конце статьи я хочу иметь возможность зайти в консоль и выполнить:

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

✅ Решение: Object.defineProperty()

нам нужно знатьObject.defineProperty()функция, которая представляет собой простой JavaScript ES5. Это позволяет нам определять функции получения и установки для свойств. Прежде чем я покажу вам, как использовать ее в классе Dep, позвольте мне кратко показать ее использование.

Как вы можете видеть, он регистрирует только две строки. Однако на самом деле он не получает и не устанавливает никакого значения, потому что мы злоупотребляем этой функцией. Давайте добавим его обратно сейчас. get() ожидает возврата значения, а set() по-прежнему нуждается в обновлении значения, поэтому давайте добавим переменную internalValue для хранения нашего текущего значения цены.

Теперь, когда наши get и set работают, как вы думаете, что будет выведено на консоль?

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

К вашему сведению, Object.keys(data) возвращает массив ключей объекта.

У всего теперь есть геттеры и сеттеры, и мы видим это на консоли.

🛠 Соединяем обе идеи вместе

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

Get => Запомните эту анонимную функцию, когда наше значение изменится, мы снова запустим ее.

Установить => запустить сохраненную анонимную функцию, наше значение просто изменилось.

Или в случае с нашим Dep Class

Price accessed (get)=> вызовите dep.depend(), чтобы сохранить текущую цель

Price set=> вызвать dep.notify() по цене, перезапустить все цели

Давайте объединим эти две идеи и завершим наш окончательный код.

Теперь посмотрите, что происходит.

Именно то, что мы хотели! Цены и количество действительно ответили в режиме реального времени! Наш общий код перезапускается всякий раз, когда обновляется значение цены или количества.

Эта иллюстрация в документах Vue должна начать обретать смысл сейчас.

Видите этот красивый фиолетовый кружок данных с геттерами и сеттерами? Это должно выглядеть знакомо! У каждого экземпляра компонента есть экземпляр наблюдателя (синий), который собирает зависимости от геттеров (красные линии). Когда установщик вызывается позже, он уведомляет наблюдателя о необходимости повторного рендеринга компонента. Вот изображение некоторых моих собственных аннотаций.

Да, разве это не имеет больше смысла сейчас?

Очевидно, что то, что делает Vue, может быть более сложным и удивительным, но теперь вы знаете основы.

⏪ Резюме: Итак, что мы узнали?

  • Как создать класс Dep для сбора зависимостей (dependencies) и перезапуска всех зависимостей (notify).
  • Как создать наблюдатель для управления нашим работающим кодом, который может потребоваться добавить в качестве зависимости (цели).
  • Как создавать геттеры и сеттеры с помощью Object.defineProperty().