логотип PurpleSchool
логотип PurpleSchool

Структура и особенности Single File Components SFC в Vue

Автор

Олег Марков

Введение

Single File Components (SFC) – это один из ключевых подходов организации кода во Vue. Вы получили возможность изолированно описывать структуру, логику и стили своих компонентов в одном файле, что делает разработку более понятной и модульной. SFC часто имеют расширение .vue и представляют собой уникальный формат файлов, в которых объединяется сразу несколько аспектов работы с компонентом.

В этой статье я подробно расскажу, что такое SFC, покажу их структуру и объясню, почему их использование значительно упрощает создание масштабируемых и поддерживаемых приложений. Мы рассмотрим типовой шаблон SFC, особенности каждого блока, инструменты и самые полезные возможности, включая новые возможности Vue 3. Я добавлю наглядные примеры и прокомментирую каждый из них, чтобы вы сразу смогли применить знания на практике.

Что такое Single File Component (SFC) во Vue

SFC — это своего рода контейнер, который объединяет три главные части любого компонента: HTML-шаблон, JavaScript-логику и CSS-стили. Такой подход позволяет концентрировать всю логику, разметку и внешний вид компонента в одном месте.

Если сравнивать с другими подходами (например, раздельные шаблоны и js-файлы), SFC делает код более компактным, снижает связанность и облегчает повторное использование компонентой логики.

Структура файла SFC

Базовая структура SFC-файла выглядит следующим образом:

<template>
  <!-- Здесь разметка компонента -->
</template>

<script>
  // Здесь логика и описание компонента
</script>

<style>
  /* Здесь стили, относящиеся только к этому компоненту */
</style>

Каждый из этих блоков можно расширять, настраивать и использовать в различных вариациях. Давайте рассмотрим каждый блок подробно.

Блок <template>: Шаблон компонента

Блок <template> отвечает за разметку компонента. Здесь вы описываете, как будет выглядеть интерфейс вашего компонента на странице. Данные внутри шаблона связываются с логикой компонента через синтаксис двойных фигурных скобок (интерполяцию), директивы (v-if, v-for и т.д.) и привязки.

Пример шаблона:

<template>
  <div class="counter">
    <h2>{{ title }}</h2>
    <button @click="increment">+</button>
    <span>{{ count }}</span>
    <button @click="decrement">-</button>
  </div>
</template>

// Здесь разметка создает простой счетчик с заголовком, кнопками и выводом значения. Обратите внимание на директиву @click, которая связывает метод из логики компонента с действием пользователя.

Особенности блока <template>

  • Может содержать только ОДИН корневой элемент (например, всё должно быть обернуто в <div>, <section> и др.).
  • Поддерживает все стандартные Vue-директивы.
  • Не допускает выполнение "сырых" JavaScript-выражений — только то, что разрешено синтаксисом Vue.

Блок <script>: Логика и поведение

В блоке <script> описывается поведение компонента: его данные, методы, вычисляемые свойства, события жизненного цикла, импорты других компонентов и многое другое. В Vue 2 и Vue 3 есть различия в синтаксисе, поэтому я покажу оба варианта.

Классический синтаксис (Vue 2 / вариант с Options API)

<script>
export default {
  name: 'Counter',
  props: {
    title: {
      type: String,
      default: 'Счетчик'
    }
  },
  data() {
    return {
      count: 0
    }
  },
  methods: {
    increment() {
      this.count++
    },
    decrement() {
      this.count--
    }
  }
}
</script>

// В этом примере мы определяем данные компонента (count), методы (increment, decrement), принимаем внешний prop (title) и экспортируем компонент как объект.

Синтаксис Composition API (Vue 3)

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

// Используем ref для создания реактивной переменной count
const count = ref(0)

defineProps({
  title: {
    type: String,
    default: 'Счетчик'
  }
})

function increment() {
  count.value++
}

function decrement() {
  count.value--
}
</script>

// Здесь используется <script setup>, который доступен только в Vue 3. Такой синтаксис короче, логика декларативна, а все переменные и функции доступны в шаблоне без лишних return.

Особенности и расширения блока <script>

  • Можно использовать несколько блоков <script> (например, для типизации через TypeScript).
  • Поддерживается импорт других файлов, компонентов, утилит.
  • Внутри возможно объявление слотов, обработка событий жизненного цикла, настройка provide/inject и др.

Блок <style>: Стилизация

Этот блок отвечает за стили только текущего компонента. По умолчанию стили применяются ко всему приложению (глобально), но обычно применяется атрибут scoped, чтобы ограничить область стилей только этим компонентом.

<style scoped>
.counter {
  display: flex;
  align-items: center;
  gap: 8px;
}
button {
  background: #eef;
  border: 1px solid #69c;
  border-radius: 4px;
  cursor: pointer;
}
</style>

// Атрибут scoped гарантирует, что стили не затронут другие компоненты. Vue это реализует с помощью уникальных data-атрибутов на элементах.

Дополнительные возможности стилей

  • Можно добавить атрибут lang для поддержки препроцессоров (например, SCSS или LESS):
<style lang="scss" scoped>
.counter {
  color: darken(#69c, 10%);
}
</style>
  • Разрешается несколько блоков <style> для разных целей (например, отдельный блок для глобальных стилей через :global).
  • Можно использовать CSS-модули с module:
<style module>
.title {
  font-size: 2em;
}
</style>

Дополнительные блоки и расширения SFC

Блоки <script lang="ts"> и <script setup lang="ts">

Vue SFC поддерживает TypeScript прямо “из коробки”. Просто укажите нужный атрибут:

<script lang="ts">
import { defineComponent } from 'vue'

export default defineComponent({
  // Код компонента с типами TypeScript
})
</script>

Аналогично работает с <script setup lang="ts">. Такой подход значительно улучшает автодополнение и статическую проверку кода.

Глобальные и локальные стили

Можно добавить <style> без scoped, чтобы стили оказали влияние на все приложение. Этот подход используют для базовых тем оформления или глобальных utility-классов.

Использование слотов и именованных слотов

SFC полностью поддерживают систему слотов для передачи произвольной разметки в компоненты, а также именованные слоты для более сложных шаблонов:

<template>
  <div>
    <slot name="header"></slot>
    <main>
      <slot></slot>
    </main>
    <slot name="footer"></slot>
  </div>
</template>

Расширение SFC с помощью Custom Blocks

Vue SFC допускает определение пользовательских блоков, которые не используются непосредственно фреймворком, но могут быть обработаны вашими инструментами сборки (например, документирование через <docs> или тестирование через <test>). Обычно это применяется в крупных командах и больших проектах.

SFC и инструменты: Vite, Webpack, Vue Loader

Чтобы проекты на Vue корректно работали с .vue-файлами, используется специальная система сборки (Vue Loader для Webpack или vite-plugin-vue2/3 для Vite).

Смотрите пример настройки для Vite:

// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [vue()]
})

// Без специального плагина SFC-файлы не соберутся в единый JavaScript-бандл.

Особенности работы с SFC

Преимущества организации кода в SFC

  • Изоляция: шаблон, логика и стили компонента в одном месте.
  • Переиспользуемость: легко импортировать и применять тот же компонент в разных местах.
  • Лучшая поддерживаемость: все изменения для компонента – в одном файле.
  • Легче тестировать: код разделён на независимые файлы-компоненты.

Ограничения и особенности

  • Файлы SFC должны обрабатываться сборщиком. В чистом JS-файле стандартных браузерах такой компонент не запустится.
  • Код внутри <script> не видит объявления из <style> и <template> (всё компилируется отдельно).
  • Глобальные переменные или стили следует выносить в отдельные файлы/директории.

Работа с несколькими SFC и импорт компонентов

В большом проекте компонентов SFC обычно много. Импортировать их просто:

<script>
import MyButton from './MyButton.vue'

export default {
  components: {
    MyButton
  }
}
</script>

<template>
  <div>
    <my-button />
  </div>
</template>

Автоматическая регистрация компонентов

Многие сборщики (например, Vite) поддерживают автоматическую регистрацию компонентов, если настроить соответствующий плагин. Это удобно при большом количестве компонентов:

// vite.config.js
import Components from 'unplugin-vue-components/vite'

export default {
  plugins: [Components()]
}

Аннотация: Инструменты для работы с SFC

  • Vetur и Volar: расширения для редакторов (VS Code) для поддержки подсветки, автодополнения, проверки типов.
  • Vue Devtools: инструменты для инспекции состояния компонентов при работе в браузере.
  • vue-jest и @vue/test-utils: для юнит-тестирования логики компонентов в SFC.

Практические рекомендации по использованию SFC

  1. Разделяйте крупные компоненты на более мелкие, чтобы улучшить читаемость и поддержку.
  2. Используйте scoped стили для локализации оформления, но не для глобальных тем/utility-классов.
  3. Следуйте единым стилевым гайдлайнам для кода и структуры компонентов (например, Vue Style Guide).
  4. Для переиспользуемых логик применяйте composables (Vue 3).
  5. Размещайте тесты и документацию рядом с SFC (например, в соседних файлах).

Заключение

Single File Components в Vue сделали процесс фронтенд-разработки структурированным, модульным, а главное — значительно более простым для поддержки и масштабирования. Вы можете вынести всю разметку, логику и стилизацию компонента в один изолированный файл, следуя современной архитектуре. Гибкость настройки и совместимость с современными инструментами разработки позволят интегрировать SFC практически в любой рабочий процесс. Понимание структуры SFC обязательно повысит вашу продуктивность и качество кода при работе с Vue-приложениями.

Частозадаваемые технические вопросы по теме статьи и ответы на них

Как подключить стили из внешнего CSS/SCSS-файла только для одного SFC?

Можно использовать импорт прямо внутри <style> с нужным препроцессором и scoped. Например:

<style scoped lang="scss">
@import './my-component-styles.scss';
// Остальной CSS
</style>

Этот стиль будет применяться только к элементам текущего компонента, если есть атрибут scoped.

Можно ли использовать несколько тегов <template> или <script> в одном SFC?

Только один <template> поддерживается. Для <script> вы можете использовать два блока, например <script> и <script setup>, но они не должны конфликтовать между собой (обычно так пишут только для миграции или документации).

Как типизировать props и emits при использовании Composition API и <script setup>?

Используйте defineProps и defineEmits с поддержкой TypeScript:

<script setup lang="ts">
interface Props {
  title: string
}
const props = defineProps<Props>()
const emit = defineEmits<{
  (e: 'update', value: number): void
}>()
</script>

Почему мои scoped-стили не применяются к тизер-контенту, приходящему через slot?

Scoped работают только для шаблона компонента. Контент, переданный в slot, принадлежит родителю. Если нужно стилизовать слот-контент, используйте классы/обертки или provide/inject для передачи данных о стилях.

Можно ли использовать Reactivity Transform внутри <script setup> SFC?

Да, с поддержкой новой опции reactivityTransform можно отказаться от .value для ref, но потребуется дополнительная настройка сборщика и/или плагины (например, для Vite это experimental компиляция).

Эти мини-инструкции помогут избежать частых ошибок и улучшить вашу работу с Single File Components в Vue.

Стрелочка влевоРабота со стилями и стилизацией в VueРабота со SCSS в проектах на Vue для стилизацииСтрелочка вправо

Все гайды по Vue

Руководство по валидации форм во Vue.jsИнтеграция Tiptap для создания редакторов на VueРабота с таблицами во Vue через TanStackИнструкция по установке и компонентам Vue sliderУправление пакетами Vue js с помощью npmУправление пакетами и node modules в Vue проектахКак использовать meta для улучшения SEO на VueПолный гайд по компоненту messages во Vuejs5 правил использования Inertia с Vue и LaravelРабота с модулями и пакетами в VueИнструкция по работе с grid на VueGithub для Vue проектов - подробная инструкция по хранению и совместной работеНастройка ESLint для Vue проектов и поддержка качества кодаОбработка ошибок и отладка в Vue.jsИспользование Vue Devtools для отладки и мониторинга приложенийРабота с конфигурационными файлами и скриптами VueСоздание и настройка проектов Vue с помощью Vue CLI3 способа интеграции Chart.js с Vue для создания графиковРабота с Canvas во VueИнструкция по реализации календаря во VueРабота с Ant Design Vue для создания UI на Vue
Обзор и использование утилит Vue для удобной разработкиРабота с обновлениями компонента и жизненным циклом updateРазрешение конфликтов и ошибок с помощью Vue resolveИспользование query-параметров и их обработка в маршрутах VueЗагрузка и управление состоянием загрузки в VueИспользование библиотек Vue для расширения функционалаРабота с JSON данными в приложениях VueКак работать с экземплярами компонента Instance во VueПолучение данных и API-запросы во Vue.jsЭкспорт и импорт данных и компонентов в VueОбработка событий и их передача между компонентами VuejsГайд по defineEmits на Vue 3Понимание core функционала Vue и его применениеПонимание и применение Composition API в Vue 3Понимание и работа с компилятором VueКогда и как использовать $emit и call во VueВзаимодействие с внешними API через Axios в Vue
Веб приложения на Vue архитектура и лучшие практикиИспользование Vite для быстрого старта и сборки проектов на Vue 3Работа с URL и ссылками в приложениях на VueРабота с пользовательскими интерфейсами и UI библиотеками во VueОрганизация и структура исходных файлов в проектах VueИспользование Quasar Framework для разработки на Vue с готовыми UI-компонентамиОбзор популярных шаблонов и стартовых проектов на VueИнтеграция Vue с PHP для создания динамичных веб-приложенийКак организовать страницы и маршруты в проекте на VueNuxt JS и Vue 3 для SSR приложенийСоздание серверных приложений на Vue с помощью Nuxt jsИспользование Vue Native для разработки мобильных приложенийОрганизация и управление индексной страницей в проектах VueИспользование Docker для контейнеризации приложений на VueИнтеграция Vue.js с Django для создания полноценных веб-приложенийСоздание и работа с дистрибутивом build dist Vue приложенийРабота со стилями и CSS в Vue js для красивых интерфейсовСоздание и структурирование Vue.js приложенияКак исправить ошибку cannot find module vueНастройка и сборка проектов Vue с использованием современных инструментовИнтеграция Vue с Bitrix для корпоративных решенийРазработка административных панелей на Vue js
5 библиотек для создания tree view во VueИнтеграция Tailwind CSS с Vue для современных интерфейсовИнтеграция Vue с серверной частью и HTTPS настройкамиКак обрабатывать async операции с Promise во VueИнтеграция Node.js и Vue.js для разработки приложенийРуководство по интеграции Vue js в NET проектыПримеры использования JSX во VueГайд по импорту и регистрации компонентов на VueМногоязычные приложения на Vue с i18nИнтеграция FLIR данных с Vue5 примеров использования filter во Vue для упрощения разработки3 примера реализации drag-and-drop во Vue
Управление переменными и реактивными свойствами во VueИспользование v for и slot в VueПрименение v-bind для динамической привязки атрибутов в VueУправление пользователями и их данными в Vue приложенияхСоздание и использование UI Kit для Vue приложенийТипизация и использование TypeScript в VuejsИспользование шаблонов в Vue js для построения интерфейсовИспользование Swiper для создания слайдеров в VueРабота со стилями и стилизацией в VueСтруктура и особенности Single File Components SFC в VueРабота со SCSS в проектах на Vue для стилизацииРабота со скроллингом и прокруткой в Vue приложенияхПрименение script setup синтаксиса в Vue 3 для упрощения компонентовИспользование scoped стилей для изоляции CSS в компонентах Vue3 способа улучшить навигацию Vue с push()Обработка запросов и асинхронных операций в VueПонимание и использование provide inject для передачи данных между компонентамиПередача и использование props в Vue 3 для взаимодействия компонентовПередача данных между компонентами с помощью props в Vue jsУправление property и функциями во Vue.jsРабота со свойствами компонентов VueУправление параметрами и динамическими данными во VueРабота с lifecycle-хуком onMounted во VueОсновы работы с объектами в VueПонимание жизненного цикла компонента Vue js на примере mountedИспользование модальных окон modal в Vue приложенияхИспользование методов в компонентах Vue для обработки логикиИспользование метода map в Vue для обработки массивовИспользование хуков жизненного цикла Vue для управления состоянием компонентаРабота с ключами key в списках и компонентах VueОбработка пользовательского ввода в Vue.jsРабота с изображениями и их оптимизация в VueИспользование хуков жизненного цикла в VueОрганизация сеток и гридов для верстки интерфейсов на VueСоздание и управление формами в VueОрганизация файлов и структура проекта Vue.jsКомпоненты Vue создание передача данных события и emitРабота с динамическими компонентами и данными в Vue3 способа манипулирования DOM на VueРуководство по div во VueИспользование директив в Vue и их расширенные возможностиОсновы и применение директив в VueИспользование директив и их особенности на Vue с помощью defineИспользование компонентов datepicker в Vue для выбора датОрганизация циклов и итераций во VueКак работает компиляция Vue CoreСоздание и использование компонентов в Vue JSОбработка кликов и пользовательских событий в VueИспользование классов в Vue для организации кода и компонентовИспользование директивы checked для управления состоянием чекбоксов в VueГайд на checkbox компонент во VueОтображение данных в виде графиков с помощью Vue ChartСоздание и настройка кнопок в VueСоздание и настройка кнопок в Vue приложенияхРабота с lifecycle-хуками beforeCreate и beforeMount во VueИспользование массивов и методов их обработки в VueИспользование массивов и их обработка в Vue
Использование Vuetify для создания современных интерфейсов на VueИспользование transition во VueТестирование компонентов и приложений на VueРабота с teleport для управления DOM во VueПять шагов по настройке SSR в VuejsИспользование Shadcn UI компонентов с Vue для продвинутых интерфейсовИспользование router-link для навигации в Vue RouterКак использовать require в Vue для динамического импорта модулейРабота с динамическим рендерингом и виртуальным DOM на Vue.jsИспользование ref для управления ссылками и реактивностью в Vue 3Использование Vue Pro и его преимущества для профессиональной разработкиРуководство по nextTick для работы с DOMСоздание и использование компонентов с помощью Vue js и CУправление состоянием и реактивностью через inject и provideДинамическое обновление компонентов и данных на VueГлубокое изучение документации Vue и как эффективно её использоватьИспользование Crystal с Vue для разработкиИспользование вычисляемых свойств для динамического отображения данных на Vue jsОптимизация производительности и предупреждения в Vue
Открыть базу знаний