Руководство
Основы
- Установка
- Введение
- Экземпляр 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, документация на русском здесь.
Render-функции и JSX
Основы
В большинстве случаев для формирования HTML с помощью Vue рекомендуется использовать шаблоны. Впрочем, иногда возникает необходимость в использовании всех алгоритмических возможностей JavaScript. В таких случаях можно применить render
-функции — более низкоуровневую альтернативу шаблонам.
Давайте разберём простой пример, в котором использование render
-функции будет целесообразным. Предположим, вы хотите сгенерировать заголовки с «якорями»:
|
Для генерации представленного выше HTML вы решаете использовать такой интерфейс компонента:
|
При использовании шаблонов для реализации интерфейса, который генерирует только заголовок, основанный на свойстве level
, вы быстро столкнётесь со следующей ситуацией:
|
|
Выглядит не очень. Шаблон получился не только очень большим — приходится ещё и <slot></slot>
повторять для каждого возможного уровня заголовка.
Шаблоны хорошо подходят для большинства компонентов, но рассматриваемый сейчас — явно не один из них. Давайте попробуем переписать компонент, используя render
-функцию:
|
Так-то лучше, наверное? Код короче, но требует более подробного знакомства со свойствами экземпляра Vue. В данном случае необходимо знать, что когда дочерние элементы передаются без указания директивы v-slot
, как например Привет, мир!
внутри anchored-heading
, они сохраняются в экземпляре компонента как $slots.default
. Если вы этого ещё не сделали, советуем вам пробежать глазами API-справочник свойств экземпляра, перед тем как углубляться в рассмотрение render-функций.
Узлы, деревья, и виртуальный DOM
Прежде чем погрузиться в render
-функции, необходимо узнать, как работают браузеры. Возьмём, например, этот HTML-код:
|
Когда браузер обрабатывает этот код, он создаёт дерево «узлов DOM», чтобы облегчить ему отслеживание всего, как, например, вы могли бы построить генеалогическое дерево для отслеживания вашей увеличивающейся семьи.
Дерево узлов DOM для HTML из примера выше выглядит следующим образом:
Каждый элемент является узлом. Каждый фрагмент текста является узлом. Даже комментарии это узлы! Узел — это всего лишь часть страницы. И так же, как и в генеалогическом дереве, каждый узел может иметь своих потомков (т.е. каждая часть может содержать в себе другие части).
Обновление всех этих узлов может быть затруднительно, но, к счастью, вам не придётся делать это вручную. Вы просто вставляете в шаблон определённый HTML-код, который вы хотите отобразить на странице:
|
Или то же, но с использованием render-функции:
|
В обоих случаях Vue автоматически будет обновлять страницу при изменениях blogTitle
.
Виртуальный DOM
Vue реализует это созданием виртуального DOM, чтобы отслеживать изменения, которые ему требуется внести в реальный DOM. Рассмотрим подробнее следующую строку:
|
Что в действительности возвращает createElement
? Это не в точности реальный DOM-элемент. Можно было бы назвать createNodeDescription
, если быть точным, так как результат содержит информацию, описывающую Vue, какой именно узел должен быть отображён на странице, включая описания любых дочерних узлов. Мы называем это описание узла «виртуальным узлом», обычно сокращая до аббревиатуры VNode. «Виртуальный DOM» — это то, что мы называем всем деревом VNodes, созданным из дерева компонентов Vue.
Аргументы createElement
Следующий момент, с которым необходимо познакомиться, — это синтаксис использования возможностей шаблонизации функцией createElement
. Вот аргументы, которые принимает createElement
:
|
Подробно об объекте данных
Обратите внимание: особым образом рассматриваемые в шаблонах атрибуты v-bind:class
и v-bind:style
, и в объектах данных виртуальных узлов имеют собственные поля на верхнем уровне объектов данных. Этот объект также позволяет вам связывать обычные атрибуты HTML, а также свойства DOM, такие как innerHTML
(это заменит директиву v-html
):
|
Полный пример
Узнав всё это, мы теперь можем завершить начатый ранее компонент:
|
Ограничения
Виртуальные узлы должны быть уникальными
Все виртуальные узлы в компоненте должны быть уникальными. Это значит, что render
-функция ниже некорректна:
|
Если вы действительно хотите многократно использовать один и тот же элемент/компонент, примените функцию-фабрику. Например, следующая render
-функция полностью корректный способ для отображения 20 одинаковых абзацев:
|
Реализация возможностей шаблона с помощью JavaScript
v-if
и v-for
Функциональность, легко реализуемая в JavaScript, не требует от Vue какой-либо проприетарной альтернативы. Например, используемые в шаблонах v-if
и v-for
:
|
При использовании render
-функции это можно легко переписать с помощью if
/else
и map
:
|
v-model
В render
-функции нет прямого аналога v-model
— вы должны реализовать эту логику самостоятельно:
|
Это цена использования низкоуровневой реализации, которая в то же время предоставляет вам больше контроля над взаимодействием, чем v-model
.
События и модификаторы клавиш
Для модификаторов событий .passive
, .capture
и .once
, Vue предоставляет префиксы, которые могут быть использованы вместе с on
:
Модификаторы | Префикс |
---|---|
.passive |
& |
.capture |
! |
.once |
~ |
.capture.once или.once.capture |
~! |
Например:
|
Для всех остальных событий и модификаторов клавиш нет необходимости в префиксе, потому что вы можете просто использовать методы события в обработчике:
Модификаторы | Эквивалент в обработчике |
---|---|
.stop |
event.stopPropagation() |
.prevent |
event.preventDefault() |
.self |
if (event.target !== event.currentTarget) return |
Клавиши:.enter , .13 |
if (event.keyCode !== 13) return (измените 13 на любой другой код клавиши для модификаторов других клавиш) |
Модификаторы клавиш:.ctrl , .alt , .shift , .meta |
if (!event.ctrlKey) return (измените ctrlKey на altKey , shiftKey или metaKey соответственно) |
Пример использования всех этих модификаторов вместе:
|
Слоты
Вы можете получить доступ к статическому содержимому слотов в виде массивов VNode, используя this.$slots
:
|
И получить доступ к слотам со своей областью видимости как к функциям, возвращающим VNode, используя this.$scopedSlots
:
|
Чтобы передать слоты со своей областью видимости в дочерний компонент при помощи render
-функции, добавьте свойство scopedSlots
в данные VNode:
|
JSX
Если приходится писать много render
-функций, то такой код может утомлять:
|
Особенно в сравнении с кодом аналогичного шаблона:
|
Поэтому есть плагин для Babel, позволяющий использовать JSX во Vue, и применять синтаксис, похожий на шаблоны:
|
Сокращение createElement
до h
— распространённое соглашение в экосистеме Vue и обязательное для использования JSX. Начиная с версии 3.4.0 плагина Babel для Vue, мы автоматически внедряем const h = this.$createElement
в любой метод и геттер (не функциях или стрелочных функциях), объявленных в синтаксисе ES2015 с JSX, поэтому можно удалить параметр (h)
. В предыдущих версиях плагина, ваше приложение будет выкидывать ошибку, если h
недоступен в области видимости.
Подробную информацию о преобразовании JSX в JavaScript можно найти в документации плагина.
Функциональные компоненты
Компонент для заголовков с «якорями», который мы создали выше, довольно прост. У него нет какого-либо состояния, хуков или требующих наблюдения данных. По сути, это всего лишь функция с параметром.
В подобных случаях мы можем пометить компоненты как функциональные (опция functional
), что означает отсутствие у них состояния (нет реактивных данных) и экземпляра (нет переменной контекста this
). Функциональный компонент выглядит так:
|
Примечание: в версиях до 2.3.0 опция
props
необходима, если вы хотите использовать входные параметры в функциональном компоненте. С версии 2.3.0 и выше вы можете опустить опциюprops
и все атрибуты, найденные на узле компонента, будут неявно извлечены в качестве входных данных.Ссылка будет указывать на HTMLElement при использовании с функциональными компонентами, потому что у них нет состояния и экземпляра.
С версии 2.5.0+, если вы используете однофайловые компоненты, вы можете объявить функциональные компоненты, основанные на шаблоне таким образом:
|
Всё необходимое компоненту передаётся через context
— объект, содержащий следующие поля:
props
: Объект со всеми переданными входными параметрамиchildren
: Массив дочерних виртуальных узловslots
: Функция, возвращающая объект со слотамиscopedSlots
: (2.6.0+) Объект, содержащий все переданные слоты с ограниченной областью видимости. Также предоставляет доступ к обычным слотам в качестве функцийdata
: Объект данных целиком, переданный объекту вторым аргументомcreateElement
parent
: Ссылка на родительский компонентlisteners
: (2.3.0+) Объект, содержащий все зарегистрированные в родителе прослушиватели событий. Это просто псевдоним дляdata.on
injections
: (2.3.0+) Если используется опцияinject
, будет содержать все разрешённые инъекции.
После указания functional: true
, обновление render
-функции нашего компонента для заголовков потребует только добавления параметра context
, обновления this.$slots.default
на context.children
и замены this.level
на context.props.level
.
Поскольку функциональные компоненты — это просто функции, их отрисовка значительно быстрее.
Кроме того, они очень удобны в качестве обёрток. Например, если вам нужно:
- Выбрать один из компонентов для последующей отрисовки в данной точке
- Произвести манипуляции над дочерними элементами, входными параметрами или данными, перед тем как передать их в дочерний компонент
Вот пример компонента smart-list
, делегирующего отрисовку к более специализированным компонентам, в зависимости от переданных в него данных:
|
Передача атрибутов и событий дочерним элементам/компонентам
В обычных компонентах, атрибуты не определённые как входные параметры, автоматически добавляются к корневому элементу компонента, заменяя или правильно объединяя любые существующие атрибуты с тем же именем.
Однако функциональные компоненты требуют явного определения этого поведения:
|
Передавая context.data
вторым аргументом в createElement
, мы передаём любые атрибуты или слушатели событий, используемые в my-functional-button
. На самом деле это настолько очевидно, что для событий не требуется модификатор .native
.
Если вы используете функциональные компоненты на основе шаблонов, вам также придётся вручную добавлять атрибуты и слушатели. Поскольку у нас есть доступ к индивидуальному содержимому контекста, мы можем использовать data.attrs
для передачи любых атрибутов HTML и listeners
(псевдоним для data.on
) для передачи любых слушателей событий.
|
slots()
vs children
Вы можете задаться вопросом зачем нужны slots()
и children
одновременно. Разве не будет slots().default
возвращать тот же результат, что и children
? В некоторых случаях — да, но что если у нашего функционального компонента будут следующие дочерние элементы?
|
Для этого компонента, children
даст вам оба абзаца, slots().default
— только второй, а slots().foo
— только первый. Таким образом, наличие и children
, и slots()
позволяет выбрать, знает ли компонент о системе слотов или просто делегировать это другому компоненту, путём передачи children
.
Компиляция шаблонов
Возможно, вам будет интересно узнать, что Vue-шаблоны в действительности компилируются в render
-функцию. Обычно нет необходимости знать подобные детали реализации, но может быть любопытным посмотреть на то, как компилируются те или иные возможности шаблонов. Ниже приведена небольшая демонстрация использования метода Vue.compile
, который в режиме реального времени компилирует строки шаблонов: