Vue 3 SFC, script setup, CSS v-bind

Vue 3 Single File Component (SFC) представляет собой формат организации компонентов, в котором шаблон, логика и стили объединены в одном файле с расширением .vue. Такой подход тесно интегрирован с инструментом сборки Vite, где обработка SFC выполняется через @vitejs/plugin-vue, обеспечивая мгновенную трансформацию, HMR и оптимизацию на уровне модулей.

Типичный SFC состоит из трёх основных блоков:

<template>
  <div class="counter">
    {{ count }}
  </div>
</template>

<script>
export default {
  data() {
    return {
      count: 0
    }
  }
}
</script>

<style scoped>
.counter {
  font-size: 20px;
}
</style>

В таком виде компонент уже функционален, однако классический API Options API постепенно уступает место Composition API и более современному синтаксису script setup.

script setup и его роль в Vue 3

<script setup> — это синтаксический сахар над Composition API, который упрощает структуру компонента и устраняет необходимость явного return в setup().

Базовый пример:

<template>
  <div>
    {{ count }}
  </div>
</template>

<script setup>
import { ref } from 'vue'

const count = ref(0)
</script>

Ключевые особенности:

  • отсутствует явное объявление setup() функции
  • все объявленные переменные автоматически доступны в шаблоне
  • меньше шаблонного кода
  • улучшенная статическая оптимизация при компиляции

Реактивность в script setup

Composition API используется напрямую:

<script setup>
import { ref, computed } from 'vue'

const price = ref(100)
const quantity = ref(2)

const total = computed(() => price.value * quantity.value)
</script>

В шаблоне .value не требуется:

<template>
  <div>{{ total }}</div>
</template>

Props и Emits в script setup

Vue 3 предоставляет специальные макросы, которые обрабатываются компилятором.

defineProps

<script setup>
const props = defineProps({
  title: String,
  count: Number
})
</script>

Использование:

<template>
  <h1>{{ title }}</h1>
</template>

defineEmits

<script setup>
const emit = defineEmits(['increment'])

function handleClick() {
  emit('increment')
}
</script>
<template>
  <button @click="handleClick">+</button>
</template>

Vite и обработка SFC

Vite использует ESM-ориентированную архитектуру, что позволяет:

  • обрабатывать .vue файлы по требованию
  • выполнять HMR без полной перезагрузки страницы
  • кэшировать зависимости через esbuild
  • разделять CSS и JS на уровне модулей

@vitejs/plugin-vue выполняет компиляцию SFC в JavaScript модули, где:

  • template преобразуется в render-функцию
  • script setup инлайнятся в setup-логику
  • style извлекается в отдельные чанки или внедряется динамически

CSS в SFC и scoped стили

Обычный scoped CSS:

<style scoped>
.button {
  background: blue;
  color: white;
}
</style>

Scoped стили реализуются через добавление уникальных атрибутов:

<div class="button" data-v-123abc></div>

Это обеспечивает изоляцию стилей между компонентами без BEM или ручных соглашений.

CSS Modules в Vue SFC

Поддерживается также подход CSS Modules:

<style module>
.button {
  background: green;
}
</style>

Использование в script:

<template>
  <button :class="$style.button">OK</button>
</template>

CSS v-bind и динамические стили

Одной из наиболее мощных возможностей Vue SFC является интеграция JavaScript значений непосредственно в CSS через v-bind.

Базовая концепция CSS v-bind

<script setup>
import { ref } from 'vue'

const color = ref('red')
</script>

<template>
  <div class="box">Text</div>
</template>

<style scoped>
.box {
  color: v-bind(color);
}
</style>

Значение color становится реактивным источником для CSS, и при изменении переменной стили обновляются без ручного вмешательства.

Механизм работы CSS v-bind

При компиляции Vite и Vue преобразуют v-bind() в CSS переменные:

.box {
  color: var(--v-color);
}

А в рантайме компонент получает инлайн-стили:

<div style="--v-color: red;"></div>

Это обеспечивает:

  • реактивность без JS пересчёта классов
  • минимальные затраты на обновление DOM
  • интеграцию с scoped стилями

Использование нескольких значений v-bind

<script setup>
import { ref } from 'vue'

const width = ref('200px')
const radius = ref('12px')
</script>

<style scoped>
.card {
  width: v-bind(width);
  border-radius: v-bind(radius);
}
</style>

Условия и вычисляемые стили

CSS v-bind может работать с computed значениями:

<script setup>
import { computed, ref } from 'vue'

const active = ref(true)

const themeColor = computed(() => active.value ? 'green' : 'gray')
</script>

<style scoped>
.panel {
  background: v-bind(themeColor);
}
</style>

Ограничения CSS v-bind

Несмотря на гибкость, существуют архитектурные ограничения:

  • нельзя использовать сложные выражения прямо в CSS (например, математические операции)
  • значения должны быть сериализуемыми
  • поддерживаются только реактивные источники из <script setup>
  • не применяется вне SFC контекста

Сравнение CSS v-bind и inline styles

Inline styles:

<div :style="{ color: color }"></div>

CSS v-bind:

div {
  color: v-bind(color);
}

Различия:

  • v-bind позволяет сохранять разделение логики и стилей
  • inline styles удобнее для разовых изменений
  • v-bind лучше интегрируется с scoped CSS и архитектурой компонентов

Комбинация script setup и CSS v-bind в реальных компонентах

<script setup>
import { ref } from 'vue'

const size = ref(40)
const active = ref(false)

function toggle() {
  active.value = !active.value
}
</script>

<template>
  <button class="btn" @click="toggle">
    Toggle
  </button>
</template>

<style scoped>
.btn {
  width: v-bind(size)px;
  height: v-bind(size)px;
  background: v-bind(active ? 'blue' : 'lightgray');
}
</style>

Такой подход демонстрирует плотную интеграцию логики и стилей без необходимости вычислять классы вручную.

Роль Vite в ускорении работы SFC и CSS v-bind

Vite обеспечивает:

  • мгновенную перекомпиляцию SFC при изменении
  • обновление только затронутых модулей через HMR
  • кэширование зависимостей Composition API
  • ускоренную трансформацию CSS через PostCSS pipeline

Благодаря этому CSS v-bind обновляется мгновенно без полной перезагрузки компонента, что особенно важно при работе с интерактивными UI системами.