Директива :class принимает объект, где ключи — имена классов, а значения — условия их применения (приводятся к булевому):
<template>
<div :class="{
active: isActive,
'text-danger': hasError,
'font-bold': isImportant
}">
Содержимое
</div>
</template>
<script setup>
const isActive = ref(true)
const hasError = ref(false)
const isImportant = ref(true)
// Результат: class="active font-bold"
</script>Статический class и динамический :class можно использовать вместе — Vue объединяет их:
<!-- Всегда будет класс "btn", плюс динамические -->
<button class="btn" :class="{ 'btn-primary': isPrimary, 'btn-sm': isSmall }">
Кнопка
</button>
<!-- При isPrimary=true, isSmall=false: class="btn btn-primary" -->:class принимает и массив — полезно когда классов много или они берутся из разных переменных:
<template>
<div :class="[baseClass, statusClass, isActive ? 'active' : '']">
</div>
</template>
<script setup>
const baseClass = ref('card')
const statusClass = computed(() => isError.value ? 'card-error' : 'card-success')
const isActive = ref(true)
</script>В массиве можно смешивать строки и объекты:
<div :class="['btn', { 'btn-primary': isPrimary }, extraClass]">Директива :style принимает объект CSS-свойств. Имена в camelCase или в кавычках kebab-case:
<template>
<div :style="{
color: textColor,
fontSize: fontSize + 'px',
backgroundColor: bgColor,
'border-radius': borderRadius + 'px'
}">
Стилизованный блок
</div>
</template>
<script setup>
const textColor = ref('#333333')
const fontSize = ref(16)
const bgColor = ref('#ffffff')
const borderRadius = ref(8)
</script>:style также принимает массив объектов — они объединяются:
<div :style="[baseStyles, themeStyles, conditionalStyles]">const baseStyles = reactive({ padding: '16px', margin: '8px' })
const themeStyles = computed(() => ({
color: isDark.value ? '#fff' : '#333',
background: isDark.value ? '#1a1a2e' : '#fff',
}))Vue автоматически добавляет вендорные префиксы к CSS-свойствам в :style, если они нужны в текущем браузере (например, transform -> -webkit-transform). Вам об этом думать не нужно.
<template>
<button
class="btn"
:class="{
'btn-loading': isLoading,
'btn-success': isSuccess,
'btn-error': isError,
'btn-disabled': isDisabled
}"
:style="{
opacity: isDisabled ? 0.5 : 1,
cursor: isDisabled ? 'not-allowed' : 'pointer',
minWidth: minWidth + 'px'
}"
:disabled="isDisabled || isLoading"
@click="handleClick"
>
{{ isLoading ? 'Загрузка...' : label }}
</button>
</template>Такой подход позволяет управлять всем внешним видом кнопки через реактивные переменные, без прямого взаимодействия с DOM.
Функции для вычисления динамических классов и стилей — логика аналогичная :class и :style в Vue
// Реализуем логику :class и :style Vue на чистом JS
// --- :class с объектом ---
function resolveClassObject(classObj) {
return Object.entries(classObj)
.filter(([, condition]) => Boolean(condition))
.map(([className]) => className)
.join(' ')
}
// --- :class с массивом (может содержать строки и объекты) ---
function resolveClassArray(classArr) {
return classArr
.map(item => {
if (typeof item === 'string') return item
if (typeof item === 'object' && item !== null) return resolveClassObject(item)
return ''
})
.filter(Boolean)
.join(' ')
}
// --- Объединение статических и динамических классов ---
function mergeClasses(staticClass, dynamicClass) {
const parts = []
if (staticClass) parts.push(staticClass)
if (typeof dynamicClass === 'string') {
parts.push(dynamicClass)
} else if (Array.isArray(dynamicClass)) {
parts.push(resolveClassArray(dynamicClass))
} else if (typeof dynamicClass === 'object') {
parts.push(resolveClassObject(dynamicClass))
}
return parts.filter(Boolean).join(' ')
}
// --- :style с объектом ---
function resolveStyleObject(styleObj) {
return Object.entries(styleObj)
.map(([prop, value]) => {
// camelCase -> kebab-case
const cssProp = prop.replace(/([A-Z])/g, '-$1').toLowerCase()
return `${cssProp}: ${value}`
})
.join('; ')
}
// --- :style с массивом объектов (объединяем) ---
function resolveStyleArray(styleArr) {
const merged = Object.assign({}, ...styleArr)
return resolveStyleObject(merged)
}
// === Демонстрация ===
console.log('=== :class с объектом ===')
console.log(resolveClassObject({
btn: true,
'btn-primary': true,
'btn-disabled': false,
active: true,
}))
// btn btn-primary active
console.log('\n=== :class с массивом ===')
console.log(resolveClassArray([
'card',
{ 'card-active': true, 'card-error': false },
'card-lg',
]))
// card card-active card-lg
console.log('\n=== Статический + динамический класс ===')
const isPrimary = true
const isLoading = false
console.log(mergeClasses('btn', {
'btn-primary': isPrimary,
'btn-loading': isLoading,
}))
// btn btn-primary
console.log('\n=== :style с объектом (camelCase -> kebab-case) ===')
console.log(resolveStyleObject({
color: '#333',
fontSize: '16px',
backgroundColor: '#f5f5f5',
borderRadius: '8px',
}))
console.log('\n=== :style с массивом (слияние) ===')
const baseStyles = { padding: '16px', color: '#333' }
const themeStyles = { color: '#fff', background: '#1a1a2e' }
// Заметь: color из themeStyles перезапишет color из baseStyles
console.log(resolveStyleArray([baseStyles, themeStyles]))Директива :class принимает объект, где ключи — имена классов, а значения — условия их применения (приводятся к булевому):
<template>
<div :class="{
active: isActive,
'text-danger': hasError,
'font-bold': isImportant
}">
Содержимое
</div>
</template>
<script setup>
const isActive = ref(true)
const hasError = ref(false)
const isImportant = ref(true)
// Результат: class="active font-bold"
</script>Статический class и динамический :class можно использовать вместе — Vue объединяет их:
<!-- Всегда будет класс "btn", плюс динамические -->
<button class="btn" :class="{ 'btn-primary': isPrimary, 'btn-sm': isSmall }">
Кнопка
</button>
<!-- При isPrimary=true, isSmall=false: class="btn btn-primary" -->:class принимает и массив — полезно когда классов много или они берутся из разных переменных:
<template>
<div :class="[baseClass, statusClass, isActive ? 'active' : '']">
</div>
</template>
<script setup>
const baseClass = ref('card')
const statusClass = computed(() => isError.value ? 'card-error' : 'card-success')
const isActive = ref(true)
</script>В массиве можно смешивать строки и объекты:
<div :class="['btn', { 'btn-primary': isPrimary }, extraClass]">Директива :style принимает объект CSS-свойств. Имена в camelCase или в кавычках kebab-case:
<template>
<div :style="{
color: textColor,
fontSize: fontSize + 'px',
backgroundColor: bgColor,
'border-radius': borderRadius + 'px'
}">
Стилизованный блок
</div>
</template>
<script setup>
const textColor = ref('#333333')
const fontSize = ref(16)
const bgColor = ref('#ffffff')
const borderRadius = ref(8)
</script>:style также принимает массив объектов — они объединяются:
<div :style="[baseStyles, themeStyles, conditionalStyles]">const baseStyles = reactive({ padding: '16px', margin: '8px' })
const themeStyles = computed(() => ({
color: isDark.value ? '#fff' : '#333',
background: isDark.value ? '#1a1a2e' : '#fff',
}))Vue автоматически добавляет вендорные префиксы к CSS-свойствам в :style, если они нужны в текущем браузере (например, transform -> -webkit-transform). Вам об этом думать не нужно.
<template>
<button
class="btn"
:class="{
'btn-loading': isLoading,
'btn-success': isSuccess,
'btn-error': isError,
'btn-disabled': isDisabled
}"
:style="{
opacity: isDisabled ? 0.5 : 1,
cursor: isDisabled ? 'not-allowed' : 'pointer',
minWidth: minWidth + 'px'
}"
:disabled="isDisabled || isLoading"
@click="handleClick"
>
{{ isLoading ? 'Загрузка...' : label }}
</button>
</template>Такой подход позволяет управлять всем внешним видом кнопки через реактивные переменные, без прямого взаимодействия с DOM.
Функции для вычисления динамических классов и стилей — логика аналогичная :class и :style в Vue
// Реализуем логику :class и :style Vue на чистом JS
// --- :class с объектом ---
function resolveClassObject(classObj) {
return Object.entries(classObj)
.filter(([, condition]) => Boolean(condition))
.map(([className]) => className)
.join(' ')
}
// --- :class с массивом (может содержать строки и объекты) ---
function resolveClassArray(classArr) {
return classArr
.map(item => {
if (typeof item === 'string') return item
if (typeof item === 'object' && item !== null) return resolveClassObject(item)
return ''
})
.filter(Boolean)
.join(' ')
}
// --- Объединение статических и динамических классов ---
function mergeClasses(staticClass, dynamicClass) {
const parts = []
if (staticClass) parts.push(staticClass)
if (typeof dynamicClass === 'string') {
parts.push(dynamicClass)
} else if (Array.isArray(dynamicClass)) {
parts.push(resolveClassArray(dynamicClass))
} else if (typeof dynamicClass === 'object') {
parts.push(resolveClassObject(dynamicClass))
}
return parts.filter(Boolean).join(' ')
}
// --- :style с объектом ---
function resolveStyleObject(styleObj) {
return Object.entries(styleObj)
.map(([prop, value]) => {
// camelCase -> kebab-case
const cssProp = prop.replace(/([A-Z])/g, '-$1').toLowerCase()
return `${cssProp}: ${value}`
})
.join('; ')
}
// --- :style с массивом объектов (объединяем) ---
function resolveStyleArray(styleArr) {
const merged = Object.assign({}, ...styleArr)
return resolveStyleObject(merged)
}
// === Демонстрация ===
console.log('=== :class с объектом ===')
console.log(resolveClassObject({
btn: true,
'btn-primary': true,
'btn-disabled': false,
active: true,
}))
// btn btn-primary active
console.log('\n=== :class с массивом ===')
console.log(resolveClassArray([
'card',
{ 'card-active': true, 'card-error': false },
'card-lg',
]))
// card card-active card-lg
console.log('\n=== Статический + динамический класс ===')
const isPrimary = true
const isLoading = false
console.log(mergeClasses('btn', {
'btn-primary': isPrimary,
'btn-loading': isLoading,
}))
// btn btn-primary
console.log('\n=== :style с объектом (camelCase -> kebab-case) ===')
console.log(resolveStyleObject({
color: '#333',
fontSize: '16px',
backgroundColor: '#f5f5f5',
borderRadius: '8px',
}))
console.log('\n=== :style с массивом (слияние) ===')
const baseStyles = { padding: '16px', color: '#333' }
const themeStyles = { color: '#fff', background: '#1a1a2e' }
// Заметь: color из themeStyles перезапишет color из baseStyles
console.log(resolveStyleArray([baseStyles, themeStyles]))Напиши функцию `buildButtonClass(state)`, которая принимает объект состояния кнопки и возвращает строку с CSS-классами. Всегда добавляй класс `"btn"`. Если `state.variant` — добавь `"btn-" + state.variant` (например, `"btn-primary"`). Если `state.size` — добавь `"btn-" + state.size`. Если `state.loading === true` — добавь `"btn-loading"` и убери вариантный класс. Если `state.disabled === true` — добавь `"btn-disabled"`.
Используй условия `if (state.variant && !state.loading) classes.push("btn-" + state.variant)`. Для каждого условия добавляй нужный класс в массив `classes`, затем верни `classes.join(" ")`.
Токены для AI-помощника закончились
Купи токены чтобы задавать вопросы AI прямо в уроке