Руководство
Основы
- Установка
- Введение
- Экземпляр Vue
- Синтаксис шаблонов
- Вычисляемые свойства и слежение
- Работа с классами и стилями
- Условная отрисовка
- Отрисовка списков
- Обработка событий
- Работа с формами
- Основы компонентов
Продвинутые компоненты
- Регистрация компонентов
- Входные параметры
- Пользовательские события
- Слоты
- Динамические и асинхронные компоненты
- Обработка крайних случаев
Переходы и анимации
- Анимирование списков и появления/исчезновения
- Анимирование переходов между состояниями
Переиспользование и композиция
- Примеси
- Пользовательские директивы
- Render-функции и JSX
- Плагины
- Фильтры
Инструментарий
- Однофайловые компоненты
- Тестирование
- Поддержка TypeScript
- Публикация на production
Масштабирование
- Роутинг
- Управление состоянием приложения
- SSR. Отрисовка на стороне сервера
- Безопасность
Продвинутые темы
- Подробно о реактивности
Вопросы миграции
- Миграция с Vue 1.x
- Миграция с Vue Router 0.7.x
- Миграция с Vuex 0.6.x на 1.0
Мета
- Сравнение с другими фреймворками
- Присоединяйтесь к сообществу Vue.js!
- Познакомьтесь с командой
Эта документация для версий v2.x и ранее. Для v3.x, документация на русском здесь.
Подробно о реактивности
Мы уже разобрали большую часть основ, так что пришло время нырнуть поглубже! Одна из наиболее примечательных возможностей Vue — это ненавязчивая реактивность. Модели представляют собой простые JavaScript-объекты. По мере их изменения обновляется и представление данных, благодаря чему управление состоянием приложения становится простым и очевидным. Тем не менее, у механизма реактивности есть ряд особенностей, знакомство с которыми позволит избежать появление распространённых ошибок. В этом разделе руководства мы подробно рассмотрим низкоуровневую реализацию системы реактивности Vue.
Как отслеживаются изменения
Когда простой JavaScript-объект передаётся в экземпляр Vue в качестве опции data
, Vue обходит все его поля и превращает их в пары геттер/сеттер, используя Object.defineProperty
. Эта возможность появилась в JavaScript только в версии ES5, поэтому в более ранних версиях её эмулировать не получится — по этой-то причине Vue и не поддерживает IE8 и ниже.
Геттеры и сеттеры не видны пользователю, но именно они являются тем внутренним механизмом, который позволяет Vue отслеживать зависимости и изменения данных. К сожалению, при таком подходе выведенные в консоль браузера геттеры и сеттеры выглядят не так, как обычные объекты, поэтому для более наглядной визуализации лучше использовать инструменты разработчика Vue-devtools.
В каждый экземпляр компонента добавлен связанный с ним экземпляр наблюдателя, который помечает все поля, затронутые при отрисовке компонента, как зависимости. В дальнейшем, когда вызывается сеттер поля, помеченного как зависимость, этот сеттер уведомляет наблюдателя, который, в свою очередь, инициирует повторную отрисовку компонента.
Особенности отслеживания изменений
Вследствие ограничений JavaScript, есть виды изменений, которые Vue не может обнаружить. Однако существуют способы обойти их, чтобы сохранить реактивность.
Для объектов
Vue не может обнаружить добавление или удаление свойства. Так как Vue добавляет геттер/сеттер на этапе инициализации экземпляра, свойство должно присутствовать в объекте data
для того чтобы Vue преобразовал его и сделал реактивным. Например:
|
Во Vue нельзя динамически добавлять новые корневые реактивные свойства в уже существующий экземпляр. Тем не менее, можно добавить реактивное свойство во вложенные объекты, используя метод Vue.set(object, propertyName, value)
:
|
Также можно использовать метод экземпляра vm.$set
, который представляет собой псевдоним к глобальному Vue.set
:
|
Иногда нужно добавить несколько свойств в существующий объект, например, с помощью Object.assign()
или _.extend()
. Если поступить так, то добавленные свойства не станут реактивными. Для решения этой задачи придётся создать новый объект, содержащий поля как оригинального объекта, так и объекта-примеси:
|
Для массивов
Vue не может отследить следующие изменения в массиве:
- Прямую установку элемента по индексу:
vm.items[indexOfItem] = newValue
- Явное изменение длины массива:
vm.items.length = newLength
Например:
|
Решить первую проблему можно двумя способами, оба дадут эффект аналогичный vm.items[indexOfItem] = newValue
, плюс запустят реактивные обновления состояния приложения:
|
|
Можно использовать метод экземпляра vm.$set
, который является псевдонимом для глобального Vue.set
:
|
Для решения второй проблемы используйте splice
:
|
Объявление реактивных свойств
Поскольку Vue не позволяет динамически добавлять корневые реактивные свойства, это означает, что все корневые поля необходимо инициализировать изначально, хотя бы пустыми значениями:
|
Если не задать поле message
в опции data, Vue выведет предупреждение, что функция отрисовки пытается получить доступ к несуществующему свойству.
Существуют технические причины для этого ограничения: оно позволяет исключить целый класс граничных случаев в системе учёта зависимостей, а также упростить взаимодействие Vue с системами проверки типов. Но более важно то, что с этим ограничением становится проще поддерживать код, так как теперь объект data
можно рассматривать как схему состояния компонента. Код, в котором реактивные свойства компонента перечислены заранее, намного проще для понимания.
Асинхронная очередь обновлений
На всякий случай напомним, что во Vue обновление DOM выполняется асинхронно. Каждый раз, когда обнаруживается изменение в данных, создаётся очередь, которая используется в качестве буфера для этого и последующих изменений, происходящих в текущей итерации (“tick”) цикла событий. Если один и тот же наблюдатель срабатывает несколько раз, в очередь он попадёт всё равно лишь единожды. Благодаря использованию буфера и устранению дубликатов, вычисления и манипуляции DOM сводятся к минимуму. В следующей итерации цикла событий Vue разбирает очередь и выполняет актуальные (уже не содержащие дубликатов) обновления. На низком уровне для асинхронной постановки задач в очередь используются Promise.then
, MutationObserver
и setImmediate
, а если они недоступны, то setTimeout(fn, 0)
.
Итак, если выполнить код vm.someData = 'новое значение'
, компонент не будет отрисован сразу же. Он обновится в следующей итерации при разборе очереди. Чаще всего эту особенность можно не принимать в расчёт, но иногда бывает нужно дождаться состояния, в которое DOM перейдёт после обновления данных. Хотя прямая манипуляция DOM нежелательна, а системы в целом предпочтительнее проектировать так, чтобы в них были первичные данные, иногда всё же её не избежать. Чтобы выполнить какой-нибудь код только после того, как завершится обновление DOM, можно передать коллбэк в метод Vue.nextTick(callback)
после изменения данных. Он будет вызван после обновления DOM. Например:
|
|
Существует также метод экземпляра vm.$nextTick()
, особенно подходящий для использования внутри компонентов, поскольку он не требует обращения к глобальной переменной Vue
, а также автоматически связывает контекст this
коллбэка с текущим экземпляром Vue:
|
Поскольку $nextTick()
возвращает Promise, вы можете достичь того же, используя новый синтаксис async/await из ES2017:
|