Олег Марков
Структура и особенности 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
- Разделяйте крупные компоненты на более мелкие, чтобы улучшить читаемость и поддержку.
- Используйте
scoped
стили для локализации оформления, но не для глобальных тем/utility-классов. - Следуйте единым стилевым гайдлайнам для кода и структуры компонентов (например, Vue Style Guide).
- Для переиспользуемых логик применяйте
composables
(Vue 3). - Размещайте тесты и документацию рядом с 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.