第 18 章:样式与 scoped
学习目标
- 区分 scoped 与全局 style
- 用
:deep穿透子组件 - 用 CSS 变量做主题
- 避坑:scoped 误作用域
一、scoped 基础
vue
<template>
<p class="text">Hello</p>
</template>
<style scoped>
.text { color: red; }
</style>编译后:
html
<p class="text" data-v-abc123></p>
<style>.text[data-v-abc123] { color: red; }</style>每个组件生成唯一 hash,样式只对当前组件生效。
⚠️ 坑 1:
scoped不会影响根元素的 id 选择器,容易出错。
二、:deep 穿透子组件
vue
<style scoped>
.parent :deep(.child-class) {
color: red; /* 影响子组件的 .child-class */
}
</style>Vue 3 写法::deep(.class),Vue 2 写法是 >>> .class 或 /deep/ .class。
三、:slotted 插槽内容
vue
<style scoped>
:slotted(p) {
color: blue; /* 父组件传进来的 <p> */
}
</style>四、:global 全局
vue
<style scoped>
:global(.global-class) {
color: red;
}
</style>💡 三个
:xxx对照::slotted()= 改父传进来的 slot 内容(父→子);:global()= 跳开 scoped 加属性(全项目生效);:deep()= 改子组件内部元素(子→子)。三个搭配用,Vue 3 scoped 样式基本就齐活。
五、CSS 变量主题
vue
<!-- App.vue -->
<style>
:root {
--primary: #42b883;
--bg: #fff;
}
[data-theme="dark"] {
--primary: #35495e;
--bg: #1a1a1a;
}
</style>💡
data-theme="dark"加到<html>上(也常加<body>,但推荐根元素):
加到哪 效果 推荐 <html data-theme="dark">全文档范围生效 ⭐⭐⭐ 最常用 <body data-theme="dark">也行,但 html 自身用的变量不变(滚动条/背景可能留白边) ⭐⭐ <div id="app">只对 app 子树生效 局部换主题 ts// 推荐写到 <html> document.documentElement.setAttribute('data-theme', 'dark')
vue
<template>
<button class="btn">主色</button>
</template>
<style scoped>
.btn {
background: var(--primary);
color: #fff;
}
</style>六、CSS Modules
vue
<template>
<p :class="$style.text">CSS Modules</p>
</template>
<style module>
.text { color: red; }
</style>ts
import { useCssModule } from 'vue'
const styles = useCssModule()
console.log(styles.text) // 'text_abc123'七、动态 class
vue
<script setup lang="ts">
const active = ref(false)
</script>
<template>
<div :class="{ active, ['text-' + size]: true }" />
<div :class="[isError ? 'red' : 'green']" />
</template>八、UnoCSS / Tailwind
ts
// vite.config.ts
import UnoCSS from 'unocss/vite'
export default defineConfig({ plugins: [vue(), UnoCSS()] })vue
<template>
<button class="bg-blue-500 text-white px-4 py-2 rounded">按钮</button>
</template>⚠️ 坑 2:UnoCSS 类名必须出现在源码中,不能动态拼接,否则扫描不到。
九、避坑清单
| 坑 | 解决 |
|---|---|
| scoped 改不到子组件 | :deep |
| 父组件想改第三方组件 | :deep |
| 主题色切换 | CSS 变量 |
| 样式污染 | 命名空间 + scoped |
十、本章小结
| 技术 | 用途 |
|---|---|
scoped | 组件级样式隔离 |
:deep | 穿透子组件 |
:slotted | 插槽内容 |
:global | 局部全局 |
| CSS Variables | 主题切换 |
动手练习
- scoped 隔离:写两个组件,同名 class 互不影响
- :deep 穿透:改 Element Plus 按钮颜色
- 主题切换:用 CSS 变量实现 light / dark 切换
下一章:第 19 章:Vite + 工程化 →