文章目录
SCSS 常用语法速查
SCSS = Sass 的 SCSS 语法(与 CSS 完全兼容的超集),文件后缀
.scss。下面整理日常项目里高频使用的语法。
1. 变量 $
$primary-color: #1677ff;
$base-font: 14px;
$radius: 4px;
.btn {
color: $primary-color;
font-size: $base-font;
border-radius: $radius;
}
- 作用域:在
{}内声明的是局部变量;用!global可提升为全局。 - 默认值:
$color: #fff !default;(仅当未定义时才赋值,常用于主题覆盖)。
2. 嵌套(Nesting)
.card {
padding: 16px;
.title {
font-weight: bold;
}
// & 表示父选择器
&:hover {
background: #f5f5f5;
}
&.is-active {
border-color: $primary-color;
}
// 属性嵌套(不常用)
font: {
family: Arial;
size: 14px;
}
}
& 的用法:
| 写法 | 等价 CSS |
|---|---|
&:hover | .card:hover |
&.active | .card.active |
.dark & | .dark .card |
&__title | .card__title(BEM 常用) |
3. 模块化:@use / @forward(推荐)
Dart Sass 1.23+ 推荐用
@use替代旧的@import。
// _variables.scss
$primary: #1677ff;
$radius: 4px;
// app.scss
@use './variables'; // 默认带命名空间
@use './variables' as v; // 自定义命名空间
@use './variables' as *; // 去掉命名空间(全局展开)
.btn {
color: variables.$primary;
border-radius: v.$radius;
}
// index.scss —— 聚合导出
@forward './variables';
@forward './mixins';
- 文件名以
_开头(如_variables.scss)表示部分文件,不会单独编译。 @use引入的同一文件只会被处理一次,避免重复输出。
4. Mixin:可复用代码块
// 无参
@mixin clearfix {
&::after {
content: '';
display: block;
clear: both;
}
}
// 带参 + 默认值
@mixin flex($direction: row, $justify: center, $align: center) {
display: flex;
flex-direction: $direction;
justify-content: $justify;
align-items: $align;
}
.container {
@include clearfix;
@include flex($justify: space-between);
}
带内容块(@content)的高级用法:
@mixin respond($breakpoint) {
@media (max-width: $breakpoint) {
@content;
}
}
.title {
font-size: 24px;
@include respond(768px) {
font-size: 18px;
}
}
5. 函数 @function
@function rem($px, $base: 16px) {
@return ($px / $base) * 1rem;
}
.title {
font-size: rem(24px); // -> 1.5rem
}
与 @mixin 的区别:函数返回值,mixin 返回样式块。
6. 继承 @extend 与占位符 %
// % 开头的占位符不会单独输出 CSS
%btn-base {
display: inline-block;
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
}
.btn-primary {
@extend %btn-base;
background: $primary;
color: #fff;
}
.btn-default {
@extend %btn-base;
background: #f5f5f5;
}
实际项目优先用
@mixin,@extend在合并选择器时容易产生意外的耦合。
7. 控制流
@if / @else if / @else
@mixin theme($name) {
@if $name == dark {
background: #000;
color: #fff;
} @else if $name == light {
background: #fff;
color: #000;
} @else {
background: #f5f5f5;
}
}
@for
// through 含末尾,to 不含
@for $i from 1 through 5 {
.col-#{$i} {
width: $i * 20%;
}
}
@each(遍历列表/Map)
$colors: (
primary: #1677ff,
success: #52c41a,
danger: #ff4d4f,
);
@each $name, $color in $colors {
.text-#{$name} { color: $color; }
.bg-#{$name} { background-color: $color; }
}
@while
$i: 1;
@while $i <= 3 {
.item-#{$i} { z-index: $i; }
$i: $i + 1;
}
8. 插值 #{}
用于在选择器、属性名、字符串中拼接变量:
$prefix: 'app';
$prop: 'border';
.#{$prefix}-card {
#{$prop}-color: #ccc;
background: url('/img/#{$prefix}-bg.png');
}
9. 运算
.box {
width: 100% - 20px; // calc 风格
height: 100px * 2;
margin: 10px + 5px;
color: rgba($primary, 0.5); // 颜色函数
}
在 Dart Sass 中除法推荐使用
math.div(旧/已废弃):@use 'sass:math'; width: math.div(100%, 3);
10. 内置模块(sass:*)
@use 'sass:math';
@use 'sass:color';
@use 'sass:string';
@use 'sass:list';
@use 'sass:map';
$darker: color.adjust($primary, $lightness: -10%);
$mixed: color.mix(#fff, $primary, 50%);
$rounded: math.round(3.7); // 4
$upper: string.to-upper-case('abc'); // ABC
$len: list.length((1, 2, 3)); // 3
$val: map.get($colors, primary);
常用颜色函数:
| 函数 | 作用 |
|---|---|
lighten($c, 10%) / darken($c, 10%) | 调亮/调暗 |
rgba($c, 0.5) | 设透明度 |
mix($c1, $c2, 50%) | 颜色混合 |
color.adjust($c, $lightness: -10%) | 通用调整 |
11. Map 操作
$z-index: (
modal: 1000,
dropdown: 900,
header: 800,
);
.modal { z-index: map-get($z-index, modal); }
// 遍历
@each $key, $val in $z-index {
.z-#{$key} { z-index: $val; }
}
12. 条件指令补充
@debug $primary; // 调试输出到控制台
@warn '该 mixin 已废弃'; // 编译时警告
@error '颜色不能为空'; // 编译时报错并终止
13. @import 旧语法(仅了解,新项目不建议)
@import './variables'; // 已被 @use / @forward 取代
14. 与 Vite 项目结合(如本工程)
vite.config.ts 中:
css: {
preprocessorOptions: {
scss: {
additionalData: `@use "@/styles/color.scss" as *;`,
},
},
}
效果:所有 .scss 文件 / <style lang="scss"> 自动注入颜色变量,无需每个文件手动 @use。
Vue SFC 中作用域写法:
<style lang="scss" scoped>
@use '@/styles/variables.scss' as *;
.box {
color: $primary;
}
</style>
15. 命名 & 组织建议(小结)
styles/
_variables.scss // 变量
_mixins.scss // mixin
_functions.scss // 函数
_reset.scss // 全局 reset
index.scss // @forward 聚合
- 变量、mixin、函数文件用
_前缀; - 业务样式按组件就近写
<style lang="scss" scoped>; - 主题色/间距/字号沉淀到统一变量文件,便于换肤。
SCSS 进阶实战补充
一、BEM + SCSS 写法
BEM 命名规范:Block__Element--Modifier
// ❌ 啰嗦写法
.card { }
.card__header { }
.card__header--fixed { }
.card__title { }
.card__body { }
// ✅ SCSS 嵌套 + & 拼接
.card {
padding: 16px;
border: 1px solid #eee;
&__header {
font-weight: bold;
&--fixed {
position: sticky;
top: 0;
}
}
&__title {
font-size: 18px;
}
&__body {
padding: 12px 0;
}
// 修饰符
&--dark {
background: #000;
color: #fff;
.card__title {
color: $primary;
}
}
}
编译结果:
.card { padding: 16px; border: 1px solid #eee; }
.card__header { font-weight: bold; }
.card__header--fixed { position: sticky; top: 0; }
.card__title { font-size: 18px; }
.card__body { padding: 12px 0; }
.card--dark { background: #000; color: #fff; }
.card--dark .card__title { color: #1677ff; }
用 mixin 封装 BEM(推荐)
// _bem.scss
@mixin b($block) {
.#{$block} {
@content;
}
}
@mixin e($el) {
&__#{$el} {
@content;
}
}
@mixin m($modifier) {
&--#{$modifier} {
@content;
}
}
使用:
@use './bem' as *;
@include b(card) {
padding: 16px;
@include e(header) {
font-weight: bold;
@include m(fixed) {
position: sticky;
top: 0;
}
}
@include m(dark) {
background: #000;
}
}
二、深度选择器(Vue scoped 场景)
<style scoped> 会给每个选择器加上 [data-v-xxx] 属性,导致无法穿透到子组件 / 第三方组件库。
Vue 3 推荐::deep()
<style lang="scss" scoped>
.wrapper {
// ❌ 失效:el-input 内部的 .el-input__inner 不会被加 data-v
.el-input__inner {
border-color: red;
}
// ✅ 穿透
:deep(.el-input__inner) {
border-color: red;
}
// ✅ 也可以放在嵌套里
.form-item {
:deep(.el-input__inner) {
height: 32px;
}
}
}
</style>
其它穿透写法(仅了解)
<style scoped>
/* Vue 2 写法,Vue 3 也兼容 */
.wrapper >>> .el-input__inner { }
.wrapper /deep/ .el-input__inner { } /* SCSS 中只能用这个或 :deep */
</style>
SCSS 里不能用
>>>(会被预处理器报错),用:deep()或/deep/。
插槽内容样式 :slotted() / 全局 :global()
<style lang="scss" scoped>
// 给传入插槽的内容加样式
:slotted(.title) {
color: red;
}
// 写一段全局样式(不加 scoped 属性)
:global(.app-toast) {
z-index: 9999;
}
</style>
三、主题切换:CSS 变量 + SCSS Map
思路:SCSS Map 集中定义主题 → 编译期生成 CSS 变量 → 运行时切换
html的data-theme即可换肤,无需重新加载样式。
1. 定义主题 Map
// _themes.scss
$themes: (
light: (
bg: #ffffff,
text: #1f1f1f,
primary: #1677ff,
border: #e5e7eb,
card-bg: #f9fafb,
),
dark: (
bg: #0f172a,
text: #f1f5f9,
primary: #38bdf8,
border: #1e293b,
card-bg: #1e293b,
),
);
2. 编译生成 CSS 变量
@use 'sass:map';
@use './themes' as *;
// 遍历每个主题,输出一份变量
@each $theme-name, $theme-map in $themes {
// 根主题
$selector: if($theme-name == light, ':root', '[data-theme="#{$theme-name}"]');
#{$selector} {
@each $key, $value in $theme-map {
--color-#{$key}: #{$value};
}
}
}
编译结果:
:root {
--color-bg: #ffffff;
--color-text: #1f1f1f;
--color-primary: #1677ff;
--color-border: #e5e7eb;
--color-card-bg: #f9fafb;
}
[data-theme="dark"] {
--color-bg: #0f172a;
--color-text: #f1f5f9;
/* ... */
}
3. 业务样式直接用 CSS 变量
.card {
background: var(--color-card-bg);
color: var(--color-text);
border: 1px solid var(--color-border);
}
.btn-primary {
background: var(--color-primary);
}
4. 封装一个取值函数(可选)
@function tc($key) {
@return var(--color-#{$key});
}
.title {
color: tc(primary); // 编译为 color: var(--color-primary)
background: tc(card-bg);
}
5. 运行时切换主题
// theme.ts
export type Theme = 'light' | 'dark'
export function setTheme(theme: Theme) {
document.documentElement.dataset.theme = theme
localStorage.setItem('theme', theme)
}
export function initTheme() {
const saved = (localStorage.getItem('theme') as Theme) ?? 'light'
setTheme(saved)
}
<script setup lang="ts">
import { setTheme } from '@/theme'
</script>
<template>
<button @click="setTheme('light')">浅色</button>
<button @click="setTheme('dark')">深色</button>
</template>
6. 加过渡,切换时丝滑
* {
transition: background-color 0.3s, color 0.3s, border-color 0.3s;
}
四、纯 SCSS Map 换肤(无 CSS 变量,旧浏览器兼容)
如果项目必须支持不支持 CSS 变量的环境,可用 mixin + 编译期多套样式。
@use 'sass:map';
$themes: (
light: (bg: #fff, text: #000),
dark: (bg: #000, text: #fff),
);
// 当前 $theme-name 由调用方传入
@mixin themify {
@each $name, $map in $themes {
[data-theme='#{$name}'] & {
$theme-map: $map !global;
@content;
$theme-map: null !global;
}
}
}
@function t($key) {
@return map.get($theme-map, $key);
}
.card {
@include themify {
background: t(bg);
color: t(text);
}
}
编译产物会为每个主题各生成一份样式,体积会大一点,但兼容性最好。
五、组合实战:BEM + 主题 + 响应式
@use '@/styles/themes' as *;
@use '@/styles/mixins' as *;
.product-card {
padding: 16px;
background: var(--color-card-bg);
border: 1px solid var(--color-border);
border-radius: 8px;
&__title {
color: var(--color-text);
font-size: 18px;
@include respond(768px) {
font-size: 16px;
}
}
&__price {
color: var(--color-primary);
font-weight: bold;
}
&--featured {
border-color: var(--color-primary);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
}
选型建议
| 场景 | 推荐方案 |
|---|---|
| 现代项目 + 需要换肤 | CSS 变量 + SCSS Map 生成 |
| 不需要换肤,纯设计规范 | SCSS 变量足够 |
| 必须兼容 IE 11 等老环境 | 纯 SCSS Map + mixin(编译多套) |
| 组件库二次定制 | :deep() + CSS 变量覆盖 |
1670

被折叠的 条评论
为什么被折叠?



