Иван Сергеев
Работа со стилями и стилизацией в Vue
Введение
Стилизация пользовательского интерфейса — неотъемлемая часть фронтенд-разработки, и во Vue для этого предусмотрено множество удобных возможностей. Вы можете применять стандартные CSS-классы, использовать inline-стили, подключать глобальные таблицы стилей или создавать изолированное оформление для отдельных компонентов через scoped-стили. Помимо стандартных подходов, Vue предлагает реактивную и динамическую стилизацию, что отлично сочетается с компонентным подходом фреймворка.
Давайте разложим по полочкам, как во Vue работать со стилями, и рассмотрим самые частые сценарии для реальных проектов.
Стилизация компонентов во Vue: основные подходы
Базовые способы подключения стилей
Во Vue есть несколько основных способов стилизовать часть или весь проект:
- Глобальные CSS-файлы (подключаются в корневой точке, например, main.js)
- Локальные стили внутри
.vue
-компонента (через тег<style>
) - Scoped-стили
- Динамические классы и инлайн-стили
- CSS-препроцессоры (Sass, Less и др.)
- CSS-модули
Рассмотрим каждый подход на примерах.
Глобальные стили
Если вам нужно применить стили сразу ко всему приложению, это делается через отдельный CSS-файл. Например, создайте src/assets/styles/global.css
и подключите его в точке входа:
// main.js
import { createApp } from 'vue'
import App from './App.vue'
import './assets/styles/global.css' // Глобальные стили
createApp(App).mount('#app')
Все селекторы из этого файла будут действовать на все DOM-элементы приложения. Это удобно для базовых переменных, сброса стилей, типографики и других общих вещей.
Локальные и scoped стили
Обычные стили в компоненте
В каждом .vue
-файле можно создать тег <style>
, где можно разместить стили только для этого компонента:
<template>
<button class="my-btn">Click me</button>
</template>
<style>
.my-btn {
background: #337ab7;
color: white;
padding: 10px 20px;
border-radius: 5px;
}
</style>
Однако такой стиль считается глобальным: если в каких-то других компонентах использовать класс my-btn
, этот стиль отработает и там. Часто это не то, что нужно, и здесь пригодится scoped-стилизация.
Scoped стили: изоляция стилей
Добавив атрибут scoped
к вашему стилю, вы получите изолированное пространство для CSS этого компонента:
<template>
<button class="my-btn">Click me</button>
</template>
<style scoped>
.my-btn {
background: #337ab7;
color: white;
}
</style>
Vue реализует scoped-стилизацию с помощью атрибутов, которые автоматически добавляются к элементам и селекторам в скомпилированном коде, создавая уникальные идентификаторы. В результате, стили применяются только к элементам данного компонента.
Примечание: Scoped-стили не изолируют стили дочерних компонентов! Если вы стилизуете корневой тег компонента, дочерний компонент может не унаследовать ваш стиль, так как его структура изолирована.
Как работает scoped: быстрый разбор
Смотрите, как преобразуются стили при сборке:
<button class="my-btn" data-v-123abc>Click me</button>
а CSS превращается в
.my-btn[data-v-123abc] { /* стили */ }
Это уникальное имя атрибута защищает ваши стили от влияния на элементы из других компонентов.
Scoped-стили и глубокое воздействие
Иногда нужно пробросить стиль глубже в DOM, например, на дочерние компоненты. Для этого используются селекторы-deep.
В Vue 3 стандартным способом является ::v-deep
:
<style scoped>
.button::v-deep .inner { color: red; }
</style>
Этот селектор позволит обратиться к элементу внутри дочернего компонента.
Динамические классы и стили
Один из главных плюсов Vue — поддержка реактивных свойств для классов и стилей.
:class — динамическое управление классами
С помощью директивы :class
можно реагировать на изменения данных:
<template>
<button :class="{ active: isActive }">Toggle</button>
</template>
<script setup>
import { ref } from 'vue'
const isActive = ref(false);
</script>
Этот код добавит класс active
, если переменная isActive
равна true.
Можно использовать и строку, и массив классов:
<template>
<div :class="['base-class', dynamicClass]"></div>
</template>
<script setup>
const dynamicClass = 'highlighted'; // Класс будет добавлен вместе с 'base-class'
</script>
Такой подход идеально подходит для управления разметкой через состояние.
:style — динамические inline-стили
Похожим образом работает директива :style
, где значение может быть объектом или массивом объектов:
<template>
<div :style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
</template>
<script setup>
const activeColor = 'crimson'
const fontSize = 18
</script>
Или с реактивными данными:
<template>
<div :style="styleObject"></div>
</template>
<script setup>
import { reactive } from 'vue'
const styleObject = reactive({
backgroundColor: '#f5f5f5',
border: '1px solid #ccc'
})
</script>
Это очень удобно при работе с темами, оформлением в зависимости от входных данных или выделением элементов.
Работа с CSS-переменными
Vue активно поддерживает использование CSS-переменных (var(--variable)
). Вы можете задавать CSS-переменные локально в style, через пропсы, или даже динамически из кода:
<template>
<div class="themed" :style="themeVars">Dynamic Theming</div>
</template>
<script setup>
import { computed } from 'vue'
const mainColor = 'teal'
const themeVars = computed(() => ({
'--main-color': mainColor,
'--padding': '12px'
}))
</script>
<style scoped>
.themed {
color: var(--main-color);
padding: var(--padding);
}
</style>
Такой способ идеально подходит, если вам нужно удобно управлять цветовыми схемами или делать runtime-перемены темы.
Использование препроцессоров (Sass, Less, Stylus)
Vue CLI и Vite отлично интегрируются с CSS-препроцессорами. Просто добавьте нужные пакеты (sass
, less
, stylus
), и вы сможете писать компактный, вложенный и наглядный CSS прямо внутри <style lang="scss" scoped>
:
<style lang="scss" scoped>
$main: #148;
.button {
background: $main;
&:hover {
background: darken($main, 10%);
}
}
</style>
Не забудьте установить препроцессор:
npm install -D sass
И используйте его в компонентах.
CSS-модули
Ещё один способ добиться изоляции — CSS-модули. Во Vue 3 они поддерживаются из коробки:
<template>
<div :class="$style.title">CSS Modules Example</div>
</template>
<style module>
.title {
color: royalblue;
font-weight: bold;
}
</style>
Здесь вы обращаетесь к сгенерированному classname через $style.title
, и такой стиль будет уникален для компонента.
Дополнительно, если используете шаблонизацию через <script setup>
, импортируйте стили так:
<script setup>
defineProps(['$style'])
</script>
Это позволяет легко избегать пересечений классов.
Глобальные и локальные стили в одном проекте
Практически всегда в больших проектах сочетаются:
- глобальные стили для общих элементов
- scoped-стили для переопределений внутри компонентов
- динамические классы и инлайн-стили для живой реактивности
Это позволяет поддерживать архитектуру чистой и понятной.
Подключение сторонних CSS-фреймворков
Vue совместим с любыми CSS-фреймворками, такими как Bootstrap, Tailwind CSS, Bulma и др. Вы можете импортировать CSS-файл фреймворка в точке входа или использовать специальную обёртку для Vue:
// main.js
import 'bootstrap/dist/css/bootstrap.min.css'
Если вы используете Utility-first CSS-фреймворки вроде Tailwind, настройте его согласно официальной документации.
Стилизация через props
Иногда компоненты сами принимают данные для стилизации. Пример передачи стиля через пропсы:
<template>
<button :style="{ background: color }">Custom Button</button>
</template>
<script setup>
defineProps({
color: {
type: String,
default: '#389e0d'
}
})
</script>
Теперь при использовании компонента вы можете явно указать цвет:
<CustomButton color="#ff9a00" />
Этот стиль напрямую попадёт в инлайновое свойство кнопки.
Scoped-стили и сторонние библиотеки
Если вы используете ui-библиотеки или сторонние компоненты, которые сложно стилизовать, часто помогают селекторы с ::v-deep
или глобальные (non-scoped) стили в вашем проекте, чтобы переопределить дефолтные значения.
Ленивая загрузка стилей
Vue (особенно при работе с Vite или Webpack) умеет разбивать CSS по чанкам. Стили компонент с тегом <style scoped>
или <style module>
грузятся только если компонент реально требуется. Это уменьшает итоговый размер вашего bundle.
Лучшие практики оформления стилей в Vue
- Использовать scoped-стили для компонентов по умолчанию
- Хранить общие переменные/миксины/preflight-стили в глобальном CSS
- Для тем или крупных переключений — использовать CSS-переменные
- Стараться избегать глобальных селекторов типа
div { ... }
(за пределами глобального файла с преднамеренными сбросами) - Избегать излишнего усложнения селекторов; лучше делать короткие, ясные классы
Заключение
Стилизовать компоненты во Vue можно гибко и удобно: поддерживаются глобальные и локальные стили, есть поддержка препроцессоров, CSS-модулей и динамической стилизации любых элементов. Используйте scoped-стили для изоляции специфических селекторов, динамические директивы для реактивного оформления и глобальные таблицы стилей для фундаментальных настроек интерфейса. Благодаря множеству инструментов и best practices вы сможете поддерживать чистоту, масштабируемость и предсказуемость стилевой архитектуры вашего Vue-приложения.
Частозадаваемые технические вопросы по теме статьи и ответы на них
1. Почему стили с тегом <style scoped>
не применяются к элементам, находящимся внутри дочерних компонентов?
Scoped-стили во Vue работают только в рамках текущего компонента. Если вы хотите пробросить стиль на глубоких потомков, используйте селектор ::v-deep
:
/* Передавайте стиль внутренним элементам дочерних компонентов */
.parent ::v-deep .child-element {
color: red;
}
2. Как можно динамически переключать темы (светлая/темная) во Vue на уровне приложения?
Сделайте корневой компонент с динамическими CSS-переменными и пробрасывайте их через директиву :style
на корневой контейнер:
<template>
<div :style="themeVars">
<slot/>
</div>
</template>
<script setup>
import { computed } from 'vue'
const darkTheme = true
const themeVars = computed(() => darkTheme ?
{ '--background': '#111', '--text': '#fff' }
:
{ '--background': '#fff', '--text': '#222' }
)
</script>
Весь интерфейс внутри будет оформляться с помощью переменных.
3. Как добавить глобальные миксины в SCSS для использования во всех компонентах?
Создайте отдельный файл с миксинами/переменными и используйте настройку stylePreprocessorOptions
(Vite) или vue.config.js
(Vue CLI), чтобы автоматически импортировать его в каждый компонент.
// vite.config.js
css: {
preprocessorOptions: {
scss: {
additionalData: `@import "@/assets/styles/mixins.scss";`
}
}
}
4. Есть ли способ сменить стили компонента в зависимости от родительского класса (например — для dark mode)?
Да, используйте глобальные (non-scoped) стили или CSS-переменные. Scoped-стили не учитывают родителей. Например:
.dark-mode .my-component {
background: #222;
}
5. Почему при использовании CSS-модулей часть стилей перестает применяться к элементам, у которых класс задается напрямую через class=""?
Когда вы используете CSS-модули, стили применяются только к элементам, у которых класс динамически передан через свойство $style
, например :class="$style.title"
. Если использовать просто class="title"
, класс будет обычным, без сгенерированного уникального имени, а соответственно и стили не применятся. Всегда используйте $style
при работе с CSS-модулями.