scss语法与实战经验

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 变量运行时切换 htmldata-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 变量覆盖
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

可缺不可滥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值