CSS 与 SCSS
Nuxt 内置了完整的 CSS/SCSS 支持,通过 Vite 提供快速的样式开发体验。无论你用原生 CSS、预处理器还是原子化 CSS 框架,Nuxt 都能很好地支持。
样式方案怎么选?
| 方案 | 优点 | 缺点 | 适用场景 | |---|---|---|---|---| | 原生 CSS | 零配置、原生支持 | 无嵌套、无变量 | 简单项目 | | SCSS | 嵌套、变量、mixin | 需要安装 sass | 传统项目 | | CSS Modules | 命名安全、零冲突 | 语法略繁琐 | 组件化项目 | | Tailwind CSS | 快速开发、一致性好 | 类名长、需学习 | 快速原型、团队协作 | | UnoCSS | 按需生成、更灵活 | 社区较小 | 追求极致性能 |
Nuxt 4 的 CSS 默认支持
- 原生 CSS:开箱即用
- CSS 嵌套:Vite 已原生支持,无需 PostCSS 插件
- CSS
@import:Vite 自动处理 @media嵌套:原生支持
全局样式
通过 nuxt.config.ts 引入
export default defineNuxtConfig({
css: [
'~/app/assets/css/main.css', // 全局基础样式
'~/app/assets/css/variables.css', // CSS 变量
'~/app/assets/css/reset.css', // 样式重置
],
})css 数组的顺序很重要
后面的文件会覆盖前面的同名规则。通常把变量/重置放前面,组件样式放后面。
通过 app.vue 引入
<!-- app/app.vue -->
<style src="~/assets/css/main.css"></style>nuxt.config.ts vs app.vue 引入全局样式的区别
nuxt.config.ts的css:在所有页面和组件之前加载,始终存在app.vue的<style>:仅在app.vue渲染时加载
推荐用 nuxt.config.ts 的 css 引入全局样式
更明确且不受组件渲染影响。
Scoped 样式
组件内使用 scoped,样式只作用于当前组件:
<template>
<div class="card">
<h2 class="title">标题</h2>
<p class="content">内容</p>
</div>
</template>
<style scoped>
.card {
background: #fff;
border-radius: 8px;
padding: 16px;
}
.title {
font-size: 24px;
color: #333;
}
.content {
color: #666;
}
</style>Scoped 的原理
Vue 编译器会给组件的每个 DOM 元素添加一个唯一的 data-v-xxxxxx 属性,CSS 选择器会自动加上 [data-v-xxxxxx],确保样式只作用于当前组件。
INFO
️ Scoped 样式的注意事项
- 子组件的根元素会被父组件的 scoped 样式影响(深度选择器可改变此行为)
- v-html 内容不受 scoped 样式影响(因为 DOM 是运行时生成的)
- 性能:scoped 样式比 CSS Modules 略慢(属性选择器开销)
深度选择器——影响子组件样式
<style scoped>
/* 修改子组件的样式 */
:deep(.child-component-inner) {
color: red;
}
/* 简写 ::v-deep */
::v-deep .child-component-inner {
color: red;
}
</style>何时用深度选择器?
当你需要在父组件中定制第三方组件或子组件的内部样式时。但过度使用说明组件的样式接口设计不够好。
插槽选择器——影响插槽内容
<!-- 父组件 -->
<ChildComponent>
<p class="slot-content">插槽内容</p>
</ChildComponent>
<!-- 子组件 -->
<style scoped>
/* 影响通过插槽传入的内容 */
:slotted(.slot-content) {
color: blue;
}
</style>全局选择器——在 scoped 中写全局样式
<style scoped>
:global(.global-alert) {
color: red;
}
</style>CSS Modules
使用 CSS Modules 避免命名冲突——类名会自动哈希化:
<template>
<div :class="$style.card">
<h2 :class="$style.title">标题</h2>
</div>
</template>
<style module>
.card {
background: #fff;
}
.title {
color: #333;
}
</style>CSS Modules vs Scoped
| 特性 | CSS Modules | Scoped |
|---|---|---|
| 命名安全 | ✅ 哈希类名(_card_1a2b3) | ✅ 属性选择器 |
| JS 中访问 | ✅ $style.card | ❌ 类名是字符串 |
| 动态类名 | ✅ :class="[$style.card, { [$style.active]: isActive }]" | ✅ 更直观 |
| 可复用性 | ✅ 导入其他模块 | ❌ 仅限当前组件 |
| SSR 性能 | ✅ 更好 | 略慢 |
推荐
中大型项目优先用 CSS Modules,小型项目用 Scoped 就够了。
自定义注入名称
<template>
<div :class="styles.card">内容</div>
</template>
<style module="styles">
.card { background: #fff; }
</style>文件级 CSS Modules
文件命名为 *.module.css 或 *.module.scss:
/* app/assets/css/button.module.css */
.primary {
background: #00DC82;
color: white;
padding: 8px 16px;
border-radius: 4px;
}
.secondary {
background: #666;
color: white;
}<template>
<button :class="[styles.primary, { [styles.loading]: isLoading }]">
{{ isLoading ? '加载中...' : '提交' }}
</button>
</template>
<script setup>
import styles from '~/assets/css/button.module.css'
const isLoading = ref(false)
</script>文件级 CSS Modules 的优势
可以在多个组件中共享同一套样式,且类名自动哈希避免冲突。
预处理器
SCSS(最常用)
安装依赖:
npm install -D sass使用:
<style lang="scss" scoped>
.container {
padding: 16px;
.header {
font-size: 24px;
margin-bottom: 16px;
}
.body {
padding: 16px;
}
}
</style>全局 SCSS 变量
在所有组件中直接使用 SCSS 变量,无需手动导入:
export default defineNuxtConfig({
css: ['~/app/assets/css/main.scss'],
vite: {
css: {
preprocessorOptions: {
scss: {
additionalData: '@use "~/app/assets/css/_variables.scss" as *;',
},
},
},
},
})// app/assets/css/_variables.scss
$primary: #00DC82;
$secondary: #666;
$font-size-base: 16px;
$border-radius: 8px;
$shadow: 0 2px 8px rgba(0, 0, 0, 0.1);在任何组件中直接使用变量:
<style lang="scss" scoped>
.button {
background: $primary;
font-size: $font-size-base;
border-radius: $border-radius;
box-shadow: $shadow;
}
</style>additionalData 的原理
Vite 在编译每个 SCSS 文件时,会自动在文件顶部注入 @use "~/app/assets/css/_variables.scss" as *;。这样每个组件都能直接使用变量,无需手动导入。
INFO
️ additionalData 中不要放实际的 CSS 规则 只放变量、mixin、function 定义。因为每个组件的样式都会注入这段代码,如果有实际 CSS 规则会导致重复
SCSS Mixin
// app/assets/css/_mixins.scss
@mixin flex-center {
display: flex;
align-items: center;
justify-content: center;
}
@mixin responsive($breakpoint) {
@if $breakpoint == 'sm' { @media (min-width: 640px) { @content; } }
@if $breakpoint == 'md' { @media (min-width: 768px) { @content; } }
@if $breakpoint == 'lg' { @media (min-width: 1024px) { @content; } }
}// nuxt.config.ts
export default defineNuxtConfig({
vite: {
css: {
preprocessorOptions: {
scss: {
additionalData: `
@use "~/app/assets/css/_variables.scss" as *;
@use "~/app/assets/css/_mixins.scss" as *;
`,
},
},
},
},
})Less
npm install -D less<style lang="less" scoped>
@primary: #00DC82;
.container {
padding: 16px;
.header {
font-size: 24px;
}
}
</style>Stylus
npm install -D stylus<style lang="stylus" scoped>
$primary = #00DC82
.container
padding 16px
.header
font-size 24px
</style>PostCSS
Nuxt 内置 PostCSS,默认配置了 autoprefixer:
export default defineNuxtConfig({
postcss: {
plugins: {
'postcss-nested': {}, // 嵌套规则
'postcss-custom-media': {}, // 自定义媒体查询
},
},
})PostCSS vs SCSS
PostCSS 通过插件实现各种功能(嵌套、变量等),是"后处理器"。SCSS 是"预处理器",有自己的语法。选择一种即可,不要混用。
Tailwind CSS
最流行的原子化 CSS 框架,Nuxt 有官方模块:
npm install -D @nuxtjs/tailwindcssexport default defineNuxtConfig({
modules: ['@nuxtjs/tailwindcss'],
})<template>
<div class="bg-white rounded-lg shadow-md p-6">
<h1 class="text-2xl font-bold text-green-500">Hello</h1>
<p class="text-gray-600 mt-2">Welcome to Nuxt</p>
<button class="mt-4 px-4 py-2 bg-green-500 text-white rounded hover:bg-green-600 transition">
点击
</button>
</div>
</template>Tailwind CSS 的优势
- 开发速度快——不需要命名类名
- 设计一致——统一的间距、颜色系统
- 生产体积小——自动 Tree-shaking 未使用的样式
Nuxt + Tailwind 是目前最流行的组合
Tailwind 与 Scoped 样式配合
<template>
<div class="bg-white rounded-lg p-6">
<h1 class="text-2xl font-bold">标题</h1>
</div>
</template>
<!-- 对于 Tailwind 无法覆盖的样式,用 scoped 补充 -->
<style scoped>
/* 复杂的动画或特殊样式 */
.hero-gradient {
background: linear-gradient(135deg, #00DC82 0%, #0ea5e9 100%);
}
</style>UnoCSS
更灵活的原子化 CSS 引擎,按需生成样式:
npm install -D @unocss/nuxtexport default defineNuxtConfig({
modules: ['@unocss/nuxt'],
})UnoCSS vs Tailwind CSS
- UnoCSS:更快的编译速度、更灵活的规则定义、更小的运行时
- Tailwind CSS:更成熟的生态、更丰富的组件库、更好的 IDE 支持
如果你追求极致性能或需要自定义规则
选 UnoCSS;如果需要成熟的生态和社区支持,选 Tailwind。
动态类名
根据条件动态应用样式:
<template>
<!-- 对象语法 -->
<div :class="{ 'card-active': isActive, 'card-dark': isDark }">
内容
</div>
<!-- 数组语法 -->
<div :class="['card', isActive ? 'active' : 'inactive']">
内容
</div>
<!-- 数组 + 对象 -->
<div :class="['card', { 'card-active': isActive, 'card-dark': isDark }]">
内容
</div>
</template>CSS 变量
在全局样式中定义 CSS 变量,组件中直接使用:
/* app/assets/css/main.css */
:root {
--color-primary: #00DC82;
--color-secondary: #666;
--color-bg: #ffffff;
--color-text: #333333;
--font-size-base: 16px;
--border-radius: 8px;
--shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
/* 暗色主题 */
[data-theme="dark"] {
--color-bg: #1a1a1a;
--color-text: #e0e0e0;
--shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
}<style scoped>
.button {
background: var(--color-primary);
font-size: var(--font-size-base);
border-radius: var(--border-radius);
box-shadow: var(--shadow);
}
</style>CSS 变量 vs SCSS 变量
| 特性 | CSS 变量 | SCSS 变量 |
|---|---|---|
| 运行时 | ✅ 浏览器运行时求值 | ❌ 编译时固定 |
| 响应式 | ✅ 可通过媒体查询修改 | ❌ 编译后固定 |
| 主题切换 | ✅ 改变 :root 变量即可 | ❌ 需要重新编译 |
| JavaScript 操作 | ✅ el.style.setProperty() | ❌ 不可操作 |
| 继承 | ✅ 可继承/覆盖 | ❌ 全局常量 |
推荐
需要主题切换或运行时变化的用 CSS 变量,编译时常量用 SCSS 变量。
CSS 变量 + 主题切换
// app/composables/useTheme.ts
export function useTheme() {
const theme = useCookie<'light' | 'dark'>('theme', { default: () => 'light' })
function toggle() {
theme.value = theme.value === 'light' ? 'dark' : 'light'
document.documentElement.setAttribute('data-theme', theme.value)
}
onMounted(() => {
document.documentElement.setAttribute('data-theme', theme.value)
})
return { theme, toggle }
}知识脉络
视图与布局 → 你在这里:CSS 与 SCSS
│
├─→ 下一步:资源管理
│
├─→ 相关:图片优化(图片样式处理)
│
└─→ 相关:字体(@font-face 样式)