Skip to main content

Що таке прив'язки атрибутів у 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

Короткий приклад

html
<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 без запасного значення:

html
<!-- Неправильно: avatarUrl=undefined → src="" → 404 --> <img :src="avatarUrl" /> <!-- Правильно: запасне значення --> <img :src="avatarUrl || '/default-avatar.png'" />

Якщо значення може бути відсутнім, додавай fallback або охороняй через v-if="avatarUrl".

Булеві атрибути і рядок "false":

html
<!-- Неправильно: isDisabled=false → disabled="false" → кнопка вимкнена --> <button :disabled="isDisabled">Go</button> <!-- Правильно: null видаляє атрибут --> <button :disabled="isDisabled || null">Go</button>

Браузер вважає будь-який непорожній рядок truthy для булевих атрибутів. Передай null, щоб атрибут зник і кнопка стала активною.

Клас без умови:

html
<!-- Неправильно: клас "active" присутній завжди --> <div :class="'active'">...</div> <!-- Правильно: об'єктний синтаксис для умовних класів --> <div :class="{ active: isActive }">...</div>

Об'єктний літерал як один атрибут:

html
<!-- Неправильно: об'єкт не є валідним значенням одного атрибута --> <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

html
<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.

Середній рівень: форма входу з керуванням через стан

html
<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-формі на продакшені.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.

Дочитали статтю?