基础篇四 Nuxt4 全局样式与 CSS 模块

文章目录

写 CSS 看起来简单,但项目大了问题就来了:类名冲突、样式覆盖、主题切换......Nuxt 提供了多种样式管理方案,今天我们来梳理一下最佳实践。

一、全局样式

全局样式放在 assets 或 css 目录,在 nuxt.config.ts 中引入:

ts 复制代码
export default defineNuxtConfig({
  css: [
    '~/assets/css/main.css',
    '~/assets/css/reset.css'
  ]
})

创建 assets/css/main.css:

css 复制代码
/* 全局变量 */
:root {
  --primary-color: #00dc82;
  --text-color: #333;
  --bg-color: #fff;
  --border-radius: 4px;
}

/* 暗色主题 */
.dark {
  --primary-color: #00b36b;
  --text-color: #fff;
  --bg-color: #1a1a1a;
}

/* 全局基础样式 */
body {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  color: var(--text-color);
  background-color: var(--bg-color);
  line-height: 1.6;
}

a {
  color: var(--primary-color);
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

二、CSS 变量与主题切换

CSS 变量让主题切换变得简单:

vue 复制代码
<script setup lang="ts">
const isDark = ref(false)

const toggleTheme = () => {
  isDark.value = !isDark.value
  document.documentElement.classList.toggle('dark', isDark.value)
}

// 初始化时检查系统主题
onMounted(() => {
  isDark.value = window.matchMedia('(prefers-color-scheme: dark)').matches
  document.documentElement.classList.toggle('dark', isDark.value)
})
</script>

<template>
  <button @click="toggleTheme">
    {{ isDark ? '🌙' : '☀️' }}
  </button>
</template>

配合 @nuxtjs/color-mode 模块更方便:

bash 复制代码
pnpm add @nuxtjs/color-mode
ts 复制代码
// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@nuxtjs/color-mode']
})
vue 复制代码
<script setup lang="ts">
const colorMode = useColorMode()

const toggleTheme = () => {
  colorMode.preference = colorMode.value === 'dark' ? 'light' : 'dark'
}
</script>

<template>
  <button @click="toggleTheme">
    {{ colorMode.value === 'dark' ? '🌙' : '☀️' }}
  </button>
</template>

三、Scoped CSS

组件内的样式使用 scoped 避免污染:

vue 复制代码
<template>
  <div class="card">
    <h2 class="title">标题</h2>
    <p class="content">内容</p>
  </div>
</template>

<style scoped>
.card {
  border: 1px solid #eee;
  border-radius: var(--border-radius);
  padding: 1rem;
}

.title {
  color: var(--primary-color);
  margin-bottom: 0.5rem;
}

.content {
  color: var(--text-color);
}
</style>

scoped 会给样式加上唯一的属性选择器,不会影响其他组件。

四、CSS Modules

想要更严格的类名隔离,用 CSS Modules:

vue 复制代码
<script setup lang="ts">
import styles from './ArticleCard.module.css'
</script>

<template>
  <article :class="styles.card">
    <h2 :class="styles.title">文章标题</h2>
    <p :class="styles.content">文章内容</p>
  </article>
</template>

ArticleCard.module.css:

css 复制代码
.card {
  border: 1px solid #eee;
  padding: 1rem;
}

.title {
  color: #333;
}

.content {
  color: #666;
}

编译后类名会变成 _card_abc123 这样的唯一值。

五、Tailwind CSS

Nuxt 对 Tailwind 支持很好:

bash 复制代码
pnpm add -D @nuxtjs/tailwindcss
ts 复制代码
// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@nuxtjs/tailwindcss'],
  tailwindcss: {
    cssPath: '~/assets/css/tailwind.css',
    configPath: 'tailwind.config.ts'
  }
})

创建 tailwind.config.ts:

ts 复制代码
import type { Config } from 'tailwindcss'

export default {
  content: [
    './components/**/*.{js,vue,ts}',
    './layouts/**/*.vue',
    './pages/**/*.vue',
    './plugins/**/*.{js,ts}',
    './app.vue'
  ],
  theme: {
    extend: {
      colors: {
        primary: '#00dc82'
      }
    }
  }
} satisfies Config

现在可以直接用 Tailwind 类名:

vue 复制代码
<template>
  <div class="p-4 border rounded-lg">
    <h1 class="text-2xl font-bold text-primary">标题</h1>
    <p class="text-gray-600 mt-2">内容</p>
    <button class="mt-4 px-4 py-2 bg-primary text-white rounded hover:opacity-80">
      按钮
    </button>
  </div>
</template>

六、UnoCSS

UnoCSS 是原子化 CSS 引擎,比 Tailwind 更快:

bash 复制代码
pnpm add -D @unocss/nuxt
ts 复制代码
// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@unocss/nuxt']
})

创建 uno.config.ts:

ts 复制代码
import { defineConfig, presetUno, presetAttributify, presetIcons } from 'unocss'

export default defineConfig({
  presets: [
    presetUno(),
    presetAttributify(),
    presetIcons()
  ]
})

使用方式和 Tailwind 类似:

vue 复制代码
<template>
  <div p-4 border rounded-lg>
    <h1 text-2xl font-bold text-primary>标题</h1>
    <button mt-4 px-4 py-2 bg-primary text-white rounded hover:opacity-80>
      按钮
    </button>
  </div>
</template>

七、预处理器支持

Nuxt 支持 Sass、Less、Stylus 等预处理器:

bash 复制代码
pnpm add -D sass

使用:

vue 复制代码
<style scoped lang="scss">
$primary: #00dc82;

.card {
  border: 1px solid #eee;
  
  .title {
    color: $primary;
    
    &:hover {
      opacity: 0.8;
    }
  }
  
  .content {
    color: #666;
  }
}
</style>

全局 Sass 变量,创建 assets/css/variables.scss:

scss 复制代码
$primary: #00dc82;
$text-color: #333;

在 nuxt.config.ts 配置:

ts 复制代码
export default defineNuxtConfig({
  vite: {
    css: {
      preprocessorOptions: {
        scss: {
          additionalData: '@use "~/assets/css/variables.scss" as *;'
        }
      }
    }
  }
})

现在所有组件都能直接用这些变量:

vue 复制代码
<style scoped lang="scss">
.title {
  color: $primary;  // 直接使用,无需 import
}
</style>

八、PostCSS 配置

Nuxt 内置了 PostCSS,可以添加插件:

ts 复制代码
// nuxt.config.ts
export default defineNuxtConfig({
  postcss: {
    plugins: {
      'postcss-nested': {},  // 支持嵌套
      'autoprefixer': {}     // 自动添加前缀(默认已开启)
    }
  }
})

九、动态样式

根据状态动态改变样式:

vue 复制代码
<script setup lang="ts">
const theme = ref({
  primary: '#00dc82',
  fontSize: '14px'
})

const styleVars = computed(() => ({
  '--primary': theme.value.primary,
  '--font-size': theme.value.fontSize
}))
</script>

<template>
  <div :style="styleVars" class="container">
    <p class="text">这段文字会根据主题变化</p>
    <button @click="theme.primary = '#ff0000'">换成红色</button>
  </div>
</template>

<style scoped>
.text {
  color: var(--primary);
  font-size: var(--font-size);
}
</style>

总结

样式管理方案对比:

方案 特点 适用场景
全局 CSS 简单直接 基础样式、变量
Scoped CSS Vue 原生支持 组件内部样式
CSS Modules 类名隔离严格 大型项目
Tailwind/UnoCSS 原子化、快速 快速开发
Sass/Less 变量、嵌套 复杂样式需求

推荐组合:全局 CSS 变量 + Scoped CSS + Tailwind(可选)

下一篇聊聊静态资源管理,让你的图片、字体管理更规范。

相关文章

入门篇三:Nuxt4组件自动导入:写代码少敲一半字

入门篇二:Nuxt 4路由自动生成:告别手动配置路由的日子

延伸阅读

nuxt4完整系列,持续更新中。。,欢迎来逛逛


内容有帮助?点赞、收藏、关注三连!评论区等你 💪

相关推荐
brien919 分钟前
为什么模型标称 1M,Google Antigravity 聊到 256K 却突然失忆?我们给它做了个原生行内监控圆环
前端
修炼的dance34 分钟前
录屏关键帧间隔多少合适?六档实测,2秒到10秒小了75.6%
前端
Lank_M41 分钟前
关键帧从1秒拉到10秒,录屏小87.3%,摄像头小10.8%
前端
补码缩写43 分钟前
用 ffprobe 拆录屏,八成字节在 29 个关键帧上
前端
百思不得小李44 分钟前
一个复杂的动态表单是如何实现的
前端·设计模式
陈随易1 小时前
Cmder 停更 17 个月,2.0 支持 Windows Terminal
前端·后端·程序员
风骏时光牛马1 小时前
前沿视界
前端
kyriewen1 小时前
我扫了 814 个依赖,97 个还敢要 Node 10
前端·javascript·程序员
陈随易2 小时前
3000 万人在用的翻译插件,重写一遍就是一门生意
前端·后端·程序员
IT_陈寒2 小时前
Vue的这个响应式陷阱,差点让我加班到天亮
前端·人工智能·后端