Олег Марков
Директива v-bind в Vue.js - полное руководство для разработчика
Введение
Директива v-bind в Vue.js — это основной инструмент, который позволяет связывать данные вашего компонента с атрибутами HTML-элементов и пропсами дочерних компонентов. Проще говоря, с помощью v-bind вы можете сделать разметку "живой" и реагирующей на изменения состояния.
Смотрите, я покажу вам, чем v-bind полезен на практике:
- Привязать значение к стандартным атрибутам (href, src, disabled и т.д.)
- Динамически управлять классами и стилями
- Передавать данные в пропсы дочерних компонентов
- Работать с объектами атрибутов и массовыми привязками
- Использовать сокращенный синтаксис для компактного кода
Директива v-bind кажется простой, но у нее есть много нюансов, о которых важно знать, чтобы избежать неожиданных ошибок или лишней сложности в шаблонах. В этой статье мы подробно разберем синтаксис, различные варианты использования и типичные сценарии, с которыми вы столкнетесь в реальных проектах.
Базовый синтаксис v-bind
Общий вид директивы
Давайте начнем с самого простого использования:
<!-- Привязываем значение переменной url к атрибуту href -->
<a v-bind:href="url">Перейти</a>
Комментарии к примеру:
export default {
data() {
return {
// Здесь мы определяем свойство url в состоянии компонента
url: 'https://example.com'
}
}
}
- v-bind:href — говорит Vue, что атрибут href должен получать значение из выражения справа
- "url" — это выражение JavaScript в контексте компонента (здесь это просто имя свойства)
Как видите, если вы измените this.url в коде компонента (например, в методе), атрибут href на странице обновится автоматически.
Сокращенный синтаксис двоеточием
Чаще всего разработчики используют сокращение:
<!-- Полная форма -->
<img v-bind:src="imageUrl">
<!-- Сокращенная форма -->
<img :src="imageUrl">
Vue полностью одинаково обрабатывает обе записи. Сокращение удобнее и делает шаблон компактнее, особенно когда привязок много.
Что можно писать в выражении v-bind
Справа от v-bind вы можете использовать любое валидное JavaScript-выражение в контексте компонента:
<!-- Используем вычисление -->
<a :href="isAdmin ? adminUrl : userUrl">Профиль</a>
<!-- Вызываем метод компонента -->
<button :disabled="isButtonDisabled()">Отправить</button>
<!-- Обращаемся к вычисляемому свойству -->
<div :title="tooltipText">Наведи на меня</div>
Важный момент: выражение в v-bind всегда вычисляется в контексте экземпляра компонента. То есть вы можете использовать:
- data (свойства из data())
- computed (вычисляемые свойства)
- methods (методы)
- props (входные параметры)
Но вы не можете использовать там, например, глобальные переменные из окна браузера, если не вернете их явно из компонента.
Привязка обычных атрибутов
Примеры с базовыми атрибутами
Самый частый сценарий — динамически менять значения стандартных HTML-атрибутов.
Давайте разберемся на примере:
<template>
<div>
<!-- Привязываем ссылку -->
<a :href="link" :title="linkTitle">Перейти на сайт</a>
<!-- Управляем атрибутом disabled -->
<button :disabled="isLoading">
{{ isLoading ? 'Загрузка...' : 'Отправить' }}
</button>
<!-- Меняем src у картинки -->
<img :src="avatarUrl" :alt="userName + ' - фото профиля'">
</div>
</template>
<script>
export default {
data() {
return {
// Здесь мы определяем динамическую ссылку
link: 'https://vuejs.org',
// Здесь мы задаем текст подсказки
linkTitle: 'Официальный сайт Vue.js',
// Флаг состояния загрузки
isLoading: false,
// Ссылка на аватар пользователя
avatarUrl: '/images/user.png',
// Имя пользователя для alt
userName: 'Иван Петров'
}
}
}
</script>
Обратите внимание, что:
- Если
isLoadingстанет true, кнопка автоматически получит атрибут disabled - Если
isLoadingстанет false, атрибут disabled исчезнет вовсе, а не станет строкой "false"
Булевы атрибуты
Булевы атрибуты (disabled, readonly, required, checked и т.п.) ведут себя особым образом:
- Если выражение возвращает true — атрибут добавляется
- Если выражение возвращает false, null или undefined — атрибут удаляется
Пример:
<input
type="checkbox"
:checked="isSubscribed" <!-- Флажок отмечен только если isSubscribed === true -->
>
Вам не нужно вручную возвращать строку "true" или "false". Vue сам управляет наличием атрибута на основе булева значения.
Привязка классов через v-bind:class
Основные варианты записи
Для классов Vue предлагает несколько удобных форм записи. Смотрите, я покажу вам три основных варианта.
1. Строка
<div :class="className">Текст</div>
export default {
data() {
return {
// Здесь мы задаем строку с классами
className: 'card card-primary'
}
}
}
Подходит, если вы просто храните строку классов в переменной.
2. Объект
Объект — самый полезный вариант, когда нужно включать или отключать классы в зависимости от условий:
<div :class="cardClasses">Контент</div>
export default {
data() {
return {
isActive: true,
hasError: false
}
},
computed: {
// Здесь мы возвращаем объект с флагами
cardClasses() {
return {
'card-active': this.isActive, // класс добавится, если isActive === true
'card-error': this.hasError // класс добавится, если hasError === true
}
}
}
}
Vue пробегается по полям объекта и добавляет те классы, у которых значение true.
Вы можете писать объект прямо в шаблоне:
<button
:class="{
'btn': true, <!-- этот класс всегда будет -->
'btn-primary': isPrimary, <!-- добавится, когда isPrimary === true -->
'btn-disabled': isLoading <!-- добавится, когда isLoading === true -->
}"
>
Отправить
</button>
3. Массив
Массив удобен, когда вам нужно объединять несколько наборов классов:
<div :class="[baseClass, isActive ? activeClass : '', errorClass]">
Пример
</div>
export default {
data() {
return {
baseClass: 'card',
activeClass: 'card-active',
errorClass: 'card-error'
}
}
}
В массив можно добавлять:
- строки
- объекты (с булевыми флагами, как выше)
- результаты вычислений
Пример смешанного подхода:
<div
:class="[
'card',
sizeClass, // строка, например 'card-lg'
{ 'card-loading': isLoading } // объект, включающий класс по условию
]"
>
Загрузка данных
</div>
Совмещение классов из HTML и v-bind
Если вы хотите объединить "статические" классы из разметки и динамические из v-bind, можете спокойно смешивать:
<div class="card" :class="{ 'card-active': isActive }">
Карточка
</div>
Vue объединит оба источника классов.
Привязка инлайн-стилей через v-bind:style
Объект стилей
v-bind:style позволяет динамически назначать CSS-свойства:
<div :style="boxStyle">
Блок со стилями
</div>
export default {
data() {
return {
// Здесь мы задаем объект стилей
boxStyle: {
width: '200px',
height: '100px',
backgroundColor: 'lightblue' // camelCase для background-color
}
}
}
}
Важно: при использовании объектов в JavaScript стилевые свойства в camelCase (backgroundColor, fontSize). Vue автоматически преобразует их в kebab-case (background-color, font-size) в реальном DOM.
Динамическое изменение стилей
Вы можете строить объект стилей на основе данных:
<div :style="{
width: boxWidth + 'px', <!-- вычисляем ширину -->
height: boxHeight + 'px', <!-- вычисляем высоту -->
opacity: isDisabled ? 0.5 : 1 <!-- меняем прозрачность по условию -->
}">
Настраиваемый блок
</div>
export default {
data() {
return {
boxWidth: 300,
boxHeight: 150,
isDisabled: false
}
}
}
Массив стилей
Если вам нужно объединить несколько объектов стилей, вы можете использовать массив, аналогично классам:
<div :style="[baseStyle, hoverStyle]">
Блок
</div>
export default {
data() {
return {
baseStyle: {
color: '#333',
padding: '10px'
},
hoverStyle: {
cursor: 'pointer'
}
}
}
}
Позже вы можете переключать hoverStyle, например, в обработчиках событий.
Привязка атрибутов сразу через объект
v-bind без имени атрибута
Если вы хотите разом привязать сразу много атрибутов, вы можете использовать v-bind без указания конкретного атрибута:
<button v-bind="buttonAttrs">
Кнопка
</button>
export default {
data() {
return {
// Здесь мы определяем объект с атрибутами
buttonAttrs: {
id: 'submit-btn',
type: 'button',
title: 'Отправить форму',
disabled: false
}
}
}
}
Vue возьмет все ключи из объекта buttonAttrs и применит их как атрибуты к элементу.
Это особенно полезно, когда вы хотите пробрасывать атрибуты "как есть" из родителя в дочерний компонент или когда атрибутов много и вы хотите хранить их конфигурацию в одном месте.
Динамические атрибуты с объектом и классами
Смотрите, как можно комбинировать это с классами и стилями:
<button
v-bind="buttonAttrs" <!-- массовая привязка атрибутов -->
:class="['btn', buttonClass]" <!-- отдельная привязка классов -->
:style="buttonStyle" <!-- отдельная привязка стилей -->
>
{{ label }}
</button>
export default {
props: {
label: String
},
data() {
return {
buttonAttrs: {
type: 'button',
title: 'Динамическая кнопка'
},
buttonClass: 'btn-primary',
buttonStyle: {
padding: '8px 16px'
}
}
}
}
Динамические имена атрибутов (v-bind с аргументом из выражения)
Иногда вам нужно выбрать, какой атрибут привязать, не заранее, а по условию. Для этого в Vue есть возможность передавать аргумент v-bind как выражение.
Давайте посмотрим на пример:
<template>
<!-- Аргумент директивы динамический -->
<button
:[dynamicAttr]="value"
>
Кнопка
</button>
</template>
<script>
export default {
data() {
return {
dynamicAttr: 'title', // Здесь мы выбираем атрибут который нужно привязать
value: 'Подсказка'
}
}
}
</script>
В этом случае получится такой результат в DOM:
<button title="Подсказка">Кнопка</button>
Если вы измените dynamicAttr на "id", Vue начнет обновлять атрибут id вместо title.
Важно:
- Значение dynamicAttr должно быть строкой без двоеточий и пробелов (например, "data-id", "href", "src")
- Это работает не только для обычных атрибутов, но и для пропсов компонента (если имя пропса совпадает)
Еще один пример, где мы можем выбирать между src и href:
<component
is="a"
:[linkType]="linkValue"
>
Ссылка
</component>
export default {
data() {
return {
linkType: 'href', // Может быть 'href' или 'data-href'
linkValue: 'https://vuejs.org'
}
}
}
Привязка пропсов компонентов через v-bind
Базовая передача пропсов
v-bind активно используется с компонентами. Когда вы передаете пропс, синтаксис такой же, как для атрибута:
<!-- Родительский компонент -->
<UserCard
:name="userName"
:age="userAge"
:is-active="isActiveUser" <!-- kebab-case в шаблоне -->
/>
// Дочерний компонент UserCard
export default {
props: {
name: String,
age: Number,
isActive: Boolean
}
}
Комментарии:
export default {
data() {
return {
// Имя пользователя
userName: 'Анна',
// Возраст пользователя
userAge: 28,
// Флаг активности
isActiveUser: true
}
}
}
Здесь Vue автоматически сопоставляет :is-active с пропсом isActive (шаблон использует kebab-case, код компонента — camelCase).
Массовая передача пропсов через объект
Если в родителе есть объект с данными, который совпадает по структуре с пропсами в дочернем компоненте, вы можете передать его целиком:
<!-- Родитель -->
<UserCard v-bind="user"></UserCard>
// Родительский компонент
export default {
data() {
return {
// Здесь мы описываем объект пользователя
user: {
name: 'Сергей',
age: 35,
isActive: false
}
}
}
}
// Дочерний компонент
export default {
props: {
name: String,
age: Number,
isActive: Boolean
}
}
Vue возьмет ключи из объекта user и попробует сопоставить их с пропсами дочернего компонента.
Если вы используете Composition API и defineProps, подход будет аналогичным — Vue по ключам объекта привязывает значения к пропсам.
Объединение пропсов и атрибутов
Смотрите, как можно смешивать разные источники:
<UserCard
v-bind="user" <!-- массовая передача пропсов -->
:show-details="true" <!-- отдельный пропс -->
class="user-card" <!-- обычный HTML-класс -->
/>
В этом случае:
- name, age, isActive придут из объекта user
- showDetails придет из отдельной привязки
- класс user-card попадет в корневой элемент компонента (если не переопределено inheritAttrs)
Особенности работы с типами и приведением значений
Строки, числа и логика
При привязке через v-bind важно понимать, как Vue работает со значениями.
Если вы пишете атрибут без v-bind:
<input value="123">
Значение будет строкой "123". Но при использовании v-bind:
<input :value="123">
Из выражения справа возвращается число 123, и это именно число (тип number) внутри компонента, хотя в DOM в итоге будет строка "123", потому что HTML хранит атрибуты как строки.
То же самое с булевыми значениями:
<button :disabled="false">Кнопка</button>
Атрибут disabled вообще не будет добавлен в DOM, потому что значение — false. Если бы вы написали:
<button disabled="false">Кнопка</button>
То с точки зрения HTML кнопка была бы отключена, потому что наличие булевого атрибута уже означает true, вне зависимости от строки внутри.
Именно поэтому v-bind важен для корректной работы с булевыми атрибутами.
Привязка к data-атрибутам
Вы можете привязывать значения и к data-атрибутам:
<div
:data-id="userId"
:data-role="userRole"
>
Пользователь
</div>
export default {
data() {
return {
userId: 42,
userRole: 'admin'
}
}
}
Здесь Vue просто запишет значения в строки, но вы все равно сможете использовать исходные типы в логике компонента.
v-bind и реактивность
Как работает обновление значений
Каждый раз, когда значение, используемое в выражении v-bind, изменяется, Vue автоматически перерисовывает только те места в DOM, где есть зависимость от этого значения.
Давайте посмотрим, что происходит в примере:
<template>
<div>
<a :href="link">Ссылка</a>
<button @click="changeLink">Изменить ссылку</button>
</div>
</template>
<script>
export default {
data() {
return {
// Исходное значение ссылки
link: 'https://google.com'
}
},
methods: {
changeLink() {
// Здесь мы меняем ссылку
this.link = 'https://vuejs.org'
}
}
}
</script>
- При первом рендере href получит значение https://google.com
- После клика по кнопке метод changeLink изменит
this.link - Vue отследит изменение и обновит только атрибут href у тега a
Все это происходит реактивно, без вашего участия.
Глубокие объекты и массивы
Если вы привязываете объект или массив, Vue отслеживает изменения внутри них (с учетом ограничений версии, особенно в Vue 2). Например:
<div :class="userClasses">
Пользователь
</div>
export default {
data() {
return {
// Здесь мы описываем объект классов
userClasses: {
'user-active': true
}
}
},
methods: {
disableUser() {
// Здесь мы меняем флаг
this.userClasses['user-active'] = false
}
}
}
Когда вы вызовете disableUser, класс user-active исчезнет с элемента.
Практические примеры реального использования
Пример 1. Карточка товара
Давайте посмотрим, что происходит в следующем примере. Мы создадим простую карточку товара с несколькими видами привязок:
<template>
<article
class="product-card"
:class="{
'product-card--popular': product.isPopular,
'product-card--out-of-stock': !product.inStock
}"
>
<img
class="product-card__image"
:src="product.imageUrl"
:alt="product.title + ' - фото товара'"
>
<h2 class="product-card__title" :title="product.title">
{{ product.title }}
</h2>
<p class="product-card__price">
{{ product.price }} ₽
</p>
<button
class="product-card__button"
:disabled="!product.inStock || isAdding"
:class="{
'btn': true,
'btn-primary': product.inStock,
'btn-secondary': !product.inStock
}"
@click="addToCart"
>
{{ product.inStock ? (isAdding ? 'Добавляем...' : 'В корзину') : 'Нет в наличии' }}
</button>
</article>
</template>
<script>
export default {
props: {
// Здесь мы описываем пропс product
product: {
type: Object,
required: true
}
},
data() {
return {
// Флаг процесса добавления в корзину
isAdding: false
}
},
methods: {
addToCart() {
// Если нет товара или уже добавляем - выходим
if (!this.product.inStock || this.isAdding) return
// Помечаем что начался процесс добавления
this.isAdding = true
// Здесь могла бы быть асинхронная операция, например запрос к API
setTimeout(() => {
// Завершаем процесс
this.isAdding = false
// Генерируем событие для родителя
this.$emit('add', this.product.id)
}, 1000)
}
}
}
</script>
Здесь вы видите:
- v-bind:class для условных модификаторов карточки
- v-bind:src и v-bind:alt для картинки
- v-bind:title для подсказки
- v-bind:disabled и v-bind:class для кнопки
Все эти привязки позволяют легко менять внешний вид и поведение карточки только через изменение объекта product и флагов компонента.
Пример 2. Универсальная кнопка с массовой привязкой
Покажу вам, как можно сделать универсальную кнопку, которая принимает любые дополнительные атрибуты:
<template>
<button
class="btn"
:class="[variantClass, { 'btn--loading': loading }]"
v-bind="attrs"
:disabled="loading || attrs.disabled"
>
<span v-if="loading" class="btn__spinner"></span>
<span><slot /></span>
</button>
</template>
<script>
export default {
props: {
// Вариант кнопки
variant: {
type: String,
default: 'primary'
},
// Флаг загрузки
loading: {
type: Boolean,
default: false
},
// Дополнительные атрибуты
attrs: {
type: Object,
default: () => ({})
}
},
computed: {
// Здесь мы вычисляем класс варианта
variantClass() {
return `btn--${this.variant}`
}
}
}
</script>
Использование:
<BaseButton
:attrs="{ type: 'submit', title: 'Отправить форму', id: 'submit-btn' }"
:loading="isSubmitting"
variant="secondary"
>
Отправить
</BaseButton>
Здесь v-bind="attrs" позволяет передавать любые атрибуты, не перечисляя их по одному. При этом вы можете дополнять или переопределять некоторые из них (например, disabled).
Типичные ошибки и подводные камни
Ошибка: кавычки вокруг выражения
Иногда новички пишут так:
<a :href="'url'">Ссылка</a> <!-- неправильно -->
Здесь 'url' — это строка "url", а не значение переменной url. Если вы хотите использовать переменную, кавычки добавлять не нужно:
<a :href="url">Ссылка</a> <!-- правильно -->
Кавычки нужны только если вы явно хотите строковый литерал, собранный из частей:
<a :href="'/user/' + userId">Профиль</a>
Ошибка: использование v-bind для простого статического значения
Если значение не зависит от данных, v-bind не нужен:
<!-- Лишняя привязка -->
<button :type="'button'">Кнопка</button>
<!-- Проще и понятнее -->
<button type="button">Кнопка</button>
Используйте v-bind только тогда, когда значение действительно динамическое.
Ошибка: смешивание логики в шаблоне
В v-bind вы можете писать любые выражения, но старайтесь не добавлять туда сложную логику:
<!-- Плохо - много логики прямо в шаблоне -->
<button
:class="{
'btn': true,
'btn-primary': user && user.role === 'admin' && !isBlocked
}"
>
Кнопка
</button>
Лучше вынести логику в вычисляемое свойство:
<button :class="buttonClasses">
Кнопка
</button>
export default {
computed: {
// Здесь мы вычисляем классы для кнопки
buttonClasses() {
return {
'btn': true,
'btn-primary': this.user && this.user.role === 'admin' && !this.isBlocked
}
}
}
}
Так код легче читать и поддерживать.
Ошибка: забытый двоеточие
Иногда вы думаете, что привязали значение, но на самом деле забыли двоеточие:
<!-- Не будет работать как динамическая привязка -->
<img src="imageUrl">
В этом случае HTML получит буквальное значение "imageUrl", а не значение переменной. Проверьте, что у вас стоит :src, а не src, если вы ожидаете динамическую подстановку:
<img :src="imageUrl">
Заключение
Директива v-bind — один из ключевых инструментов в Vue.js, который делает шаблоны динамическими и связными с данными компонента. С ее помощью вы можете:
- Привязывать значения к любым HTML-атрибутам
- Гибко управлять классами и стилями через объекты и массивы
- Массово передавать атрибуты и пропсы через объекты
- Использовать динамические имена атрибутов
- Работать с булевыми атрибутами корректно, без лишних "true"/"false" в разметке
Теперь вы видите, как много сценариев покрывает одна директива. Важно использовать v-bind осознанно: не усложнять выражения в шаблоне, аккуратно работать с типами и не забывать о сокращенном синтаксисе через двоеточие. Тогда ваши компоненты будут читабельными, предсказуемыми и удобными в поддержке.
Частозадаваемые технические вопросы по теме
Вопрос 1. Как передать все атрибуты из родителя в компонент без явного перечисления?
Вы можете использовать специальный объект $attrs в дочернем компоненте и v-bind без аргумента:
<!-- Родитель -->
<BaseInput
id="email"
placeholder="Введите email"
required
/>
<!-- Дочерний компонент -->
<input v-bind="$attrs">
Комментарии:
export default {
inheritAttrs: false // Здесь мы блокируем автоматическое навешивание атрибутов на корень
}
Так все атрибуты, не объявленные как props, попадут в $attrs и будут проброшены на <input>.
Вопрос 2. Как привязать несколько data-атрибутов через объект?
Создайте объект, где ключи — имена атрибутов, и используйте v-bind:
<div v-bind="dataAttrs">Контент</div>
export default {
data() {
return {
// Здесь мы описываем объект с data-атрибутами
dataAttrs: {
'data-id': 10,
'data-role': 'admin'
}
}
}
}
Vue применит каждый ключ и значение как атрибут.
Вопрос 3. Как использовать v-bind для aria-атрибутов?
aria-атрибуты привязываются так же, как любые другие:
<button
:aria-label="label"
:aria-disabled="isDisabled.toString()"
>
{{ label }}
</button>
Важно: многие aria-атрибуты ожидают строковое значение, поэтому для булевых флагов удобно вызывать toString().
Вопрос 4. Как безопасно комбинировать class и :class если классы приходят из пропса?
Вы можете объединить статический класс, классы из пропса и динамические классы:
<div
class="base"
:class="[customClass, { 'is-active': isActive }]"
>
Контент
</div>
export default {
props: {
customClass: {
type: [String, Array, Object],
default: ''
}
},
data() {
return {
isActive: false
}
}
}
Vue корректно объединит все источники классов.
Вопрос 5. Как избежать конфликтов между v-bind и использованием v-model на том же элементе?
v-model под капотом использует v-bind для value и v-on для события input (или другого, в зависимости от типа). Если вы вручную используете v-bind:value вместе с v-model, возможен конфликт:
<!-- Потенциальная проблема -->
<input v-model="text" :value="externalValue">
Лучше выбрать один источник истины. Если нужно инициализировать модель значением, установите его в data или computed:
export default {
props: ['externalValue'],
data() {
return {
// Здесь мы инициализируем локальное состояние
text: this.externalValue
}
}
}
Постройте личный план изучения Vue до уровня Middle — бесплатно!
Vue — часть карты развития Frontend
100+ шагов развития
30 бесплатных лекций
300 бонусных рублей на счет
Бесплатные лекции
Все гайды по Vue
Лучшие курсы по теме

Vue 3 и Pinia
Антон Ларичев
TypeScript с нуля
Антон Ларичев