Vue3 动态样式
Vue 把”样式动态化”拆成三个能力:
- 样式跟着数据走:动态
class、动态style。 - 把样式锁在本组件:
<style scoped>。 - 用 JS 变量写 CSS:样式里的
v-bind()。
动态 class
class 的绑定支持字符串、对象、数组三种形式,从简单到复杂依次介绍。
字符串语法(最简单)
一个变量直接存类名,isActive 的值是什么,class 就是什么:
<script setup>
import { ref } from 'vue'
const isActive = ref('active')
</script>
<template>
<p :class="isActive">状态文本</p>
</template>
isActive 为 'active',所以渲染出的真实 HTML 是:
<p class="active">状态文本</p>
字符串语法就是”把 class 静态字符串换成表达式”,改 isActive 的值就能换类名。但它一次只能管一个类——多个状态就不好维护了,于是有下面的对象语法。
对象语法(最常用)
先看最简单的场景——只有一种状态,一个布尔变量控制一个类名:
<script setup>
import { ref } from 'vue'
const isActive = ref(true)
</script>
<template>
<p :class="{ active: isActive }">状态文本</p>
</template>
:class 里写一个 JS 对象,语法是”给哪个类名 → 由哪个布尔决定”。拆开看三部分:
:的意思:把后面的内容当表达式计算,而不是普通字符串(和class="..."的静态写法区分)。- 大括号
{}是一个 JS 对象,表示类名与条件的对应关系。 - 每项写法
"类名: 布尔条件":键是类名,值是决定加不加的布尔值,值为true就把这个类加到元素上,为false就不加。
上面 isActive 为 true,所以最终渲染出的真实 HTML 是:
<p class="active">状态文本</p>
CSS 里把样式写到 .active 上即可。以后只要改 isActive 的值,类名、样式就会自动跟着变,这就是”样式跟着数据走”。
再扩展一步——多个状态、多个类,对象里写几项就行:
<script setup>
import { ref } from 'vue'
const isActive = ref(true) // 真 → active 这个类被加上
const isError = ref(false) // 假 → error 这个类不加
</script>
<template>
<p :class="{ active: isActive, error: isError }">状态文本</p>
</template>
对照着看,isError 为 false,所以 error 不加,渲染结果和上面一样:
<p class="active">状态文本</p>
再扩展一步——类名和变量名可以不一样,比如 isError 对应类名 has-error,'has-error': isError 即可(类名带横线时需要加引号)。
数组语法(组合多个)
数组语法是把多种来源拼在一起:固定类名、字符串变量、对象都可以放进去:
<script setup>
import { ref } from 'vue'
const typeClass = ref('card') // 字符串:类名字符串变量
const hasError = ref(false) // 布尔:对象语法里的条件
</script>
<template>
<!-- 数组里可以是固定类名、字符串变量、对象 -->
<div :class="['base', typeClass, { error: hasError }]"></div>
</template>
hasError 为 false,所以渲染结果是这些类名合并、去重:
<div class="base card"></div>
数组语法最灵活,但可读性一般,一般用于”固定类 + 条件类”混搭的复杂场景。
和静态 class 共存
class="static" 与 :class 会自动合并,不用担心互相覆盖:
<div class="card" :class="{ active: isActive }"></div>
动态 style
style 用对象语法,属性名用驼峰或加引号的 kebab-case 都行:
<script setup>
import { ref } from 'vue'
const color = ref('#f60')
const fontSize = ref(16)
</script>
<template>
<p :style="{ color: color, fontSize: fontSize + 'px' }">动态样式</p>
<!-- 背景色换成用户选的颜色 -->
<div :style="{ backgroundColor: color }"></div>
</template>
单位不会自动补全,数字需要自己拼 'px' 等。
数组语法(多个样式对象合并)用得少,了解即可。
样式里的 v-bind():用 JS 变量写 CSS
Vue3 支持在 <style> 里用 v-bind() 直接引用 <script setup> 里的变量:
<script setup>
import { ref } from 'vue'
const primaryColor = ref('#f60')
</script>
<style scoped>
.button {
/* 变量改变自动更新样式 */
background: v-bind(primaryColor);
}
</style>
这是”数据驱动视觉”的利器:主题色、深浅模式切换都不用再铺一层内联样式。
scoped:把样式锁在本组件
<style scoped> 会给组件里的元素加一个随机 data-v-xxx 属性,让样式只作用于本组件的元素,避免”多个组件的样式互相污染”:
<style scoped>
.active {
color: #f60;
}
</style>
打开浏览器控制台能看到,元素上多了 data-v-xxx 那串随机属性,CSS 编译后也带上了同样的限定,从而实现”只作用于此组件”。
(它牵扯到子组件时还有更多细节,比如能否改到子组件内部的样式,等学到Vue3 组件通信入门再展开。)
速查卡片
| 场景 | 写法 |
|---|---|
| 条件加类(推荐) | :class="{ active: isActive }" |
| 多类组合 | :class="['base', cond ? 'x' : '']" |
| 动态内联样式 | :style="{ color, fontSize: n+'px' }" |
| 本地样式隔离 | <style scoped> |
| 用 JS 变量写 CSS | background: v-bind(themeColor); |