Що таке прив'язки атрибутів у Vue?
Прив'язка атрибутів (attribute binding) у Vue використовує директиву v-bind або скорочення :, щоб підключити HTML-атрибути та пропси компонентів до реактивних даних.
Теорія
TL;DR
v-bind:href="url"і:href="url"- одне й те саме;:є стандартним скороченням- На відміну від статичного
href="...", прив'язаний атрибут оновлюється в DOM автоматично при зміні даних - Використовуй
:, якщо значення береться зdata,propsабо виразу; в інших випадках пиши значення напряму - Прив'язка
nullабоundefinedповністю видаляє атрибут з DOM - Прив'язка
:disabled="false"все одно вимикає кнопку, бо браузер вважає будь-який непорожній рядок truthy
Короткий приклад
<template>
<!-- Статично: завжди один і той самий рядок -->
<a href="https://example.com">Статичне</a>
<!-- Прив'язка: оновлюється при зміні userProfileUrl -->
<a :href="userProfileUrl">Профіль</a>
<button @click="updateUrl">Змінити URL</button>
</template>
<script>
export default {
data() {
return { userProfileUrl: 'https://example.com/profile/1' };
},
methods: {
updateUrl() {
this.userProfileUrl = 'https://example.com/profile/2';
// href оновлюється в DOM автоматично, без ручних DOM-викликів
}
}
};
</script>Прив'язаний лінк одразу відображає нове значення. Статичний не змінюється ніколи.
Статичні та динамічні атрибути
Статичні атрибути, як href="static.com", фіксуються в DOM при рендері. Vue їх більше не відстежує. Прив'язки на зразок :href="url" створюють реактивне з'єднання: система реактивності Vue (у Vue 3 базується на Proxy) стежить за url, і коли значення змінюється, планувальник ставить патч у чергу, який оновлює лише цей атрибут через virtual DOM. Повного перерендеру не відбувається.
Поширена пастка: запис <button disabled="isLoading"> замість :disabled="isLoading". Без : Vue сприймає "isLoading" як звичайний рядок, а не змінну. Кнопка вимкнена завжди, незалежно від даних.
Коли використовувати
- Значення фіксоване і відоме при написанні коду: пиши напряму (
href="mailto:hi@example.com") - Значення береться з
dataабоprops::src="imageUrl" - Умовна логіка:
:disabled="!isValid" - Стиль з умовою:
:style="{ color: isError ? 'red' : 'green' }" - Передача даних у дочірній компонент:
<MyButton :count="items.length"> - Розподіл кількох атрибутів одразу:
<div v-bind="{ id: docId, class: docClass }">
Як Vue обробляє прив'язки всередині
Компілятор шаблонів перетворює :href="url" на виклик рендер-функції: h('a', { href: _ctx.url }). При монтуванні та оновленнях Proxy перехоплює запис у url, ставить завдання в чергу планувальника, і patch викликає setAttribute на DOM-вузлі. Змінюється лише цей один атрибут.
Типові помилки
Прив'язка null або undefined без запасного значення:
<!-- Неправильно: avatarUrl=undefined → src="" → 404 -->
<img :src="avatarUrl" />
<!-- Правильно: запасне значення -->
<img :src="avatarUrl || '/default-avatar.png'" />Якщо значення може бути відсутнім, додавай fallback або охороняй через v-if="avatarUrl".
Булеві атрибути і рядок "false":
<!-- Неправильно: isDisabled=false → disabled="false" → кнопка вимкнена -->
<button :disabled="isDisabled">Go</button>
<!-- Правильно: null видаляє атрибут -->
<button :disabled="isDisabled || null">Go</button>Браузер вважає будь-який непорожній рядок truthy для булевих атрибутів. Передай null, щоб атрибут зник і кнопка стала активною.
Клас без умови:
<!-- Неправильно: клас "active" присутній завжди -->
<div :class="'active'">...</div>
<!-- Правильно: об'єктний синтаксис для умовних класів -->
<div :class="{ active: isActive }">...</div>Об'єктний літерал як один атрибут:
<!-- Неправильно: об'єкт не є валідним значенням одного атрибута -->
<div :data="{ id: user.id }">...</div>
<!-- Правильно: окремий data-атрибут -->
<div :data-id="user.id">...</div>Де зустрічається в реальних проектах
- Vuetify:
:color="themeColor"на кнопках для динамічної теми - Quasar:
:dense="isMobile"на списках для адаптивного вигляду - Nuxt з i18n:
:to="localePath('/cart')"для локалізованих маршрутів - Element Plus:
:model-value="searchQuery"на полях форм - Pinia:
:items="cartStore.items"у компонентах кошика
Можливі питання на співбесіді
Q: Яка різниця між :disabled="false" і відсутністю атрибута disabled?
A: :disabled="false" встановлює disabled="false" в HTML. Браузер вважає будь-який непорожній рядок truthy для булевих атрибутів, тому кнопка залишається вимкненою. Щоб увімкнути її, використовуй :disabled="null" або просто прибери атрибут.
Q: Як Vue поводиться з undefined і null у прив'язках?
A: Обидва видаляють атрибут у Vue 3. У Vue 2 null міг відрендеритись як порожній рядок. Для необов'язкових атрибутів надійніший патерн - value || undefined.
Q: Як прив'язати кілька атрибутів одночасно?
A: Використовуй v-bind з об'єктом: <div v-bind="{ id: docId, class: docClass }">. Vue розподіляє всі ключі як окремі атрибути на елементі.
Q: Чому в Nuxt SSR виникають помилки гідратації при прив'язках?
A: Сервер рендерить з початковими статичними даними, а клієнт гідратує з реактивним станом. Якщо ці значення відрізняються в момент монтування, Vue видає помилку гідратації. Вирішення: useAsyncData для спільного стану або v-if="mounted" навколо елемента.
Приклади
Базовий: зображення з динамічним src
<template>
<img :src="catImage" :alt="`Cat ${catId}`" />
<button @click="nextCat">Наступне</button>
</template>
<script>
export default {
data() {
return {
catId: 1,
catImage: 'https://example.com/cat1.jpg'
};
},
methods: {
nextCat() {
this.catId++;
this.catImage = `https://example.com/cat${this.catId}.jpg`;
// src та alt оновлюються синхронно, без маніпуляцій з DOM
}
}
};
</script>:src і :alt реагують одночасно при зміні catId. Браузер перезавантажує зображення, бо отримує нове значення src.
Середній рівень: форма входу з керуванням через стан
<template>
<form @submit.prevent="login">
<input
:value="email"
@input="email = $event.target.value"
placeholder="email@example.com"
required
/>
<button :disabled="isSubmitting || !email.includes('@')">
{{ isSubmitting ? 'Входимо...' : 'Увійти' }}
</button>
</form>
</template>
<script>
export default {
data() {
return { email: '', isSubmitting: false };
},
methods: {
async login() {
this.isSubmitting = true;
await new Promise(r => setTimeout(r, 1000)); // імітація API-запиту
this.isSubmitting = false;
// кнопка повертається в активний стан автоматично
}
}
};
</script>Вираз у :disabled одразу покриває дві умови: невалідний email і запит у процесі. Жодного document.querySelector. Цей патерн зустрічається майже в кожній Vue-формі на продакшені.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.