目录

七vue3后台项目系列包装scss全句变量scss与导入

七、vue3后台项目系列——包装scss、全句变量scss与导入

直接进入正题:使用scss的好处是方便复用样式,并且统一修改,只要需要修改一处,全部引用就能生效。

一、解决原生 CSS 的核心痛点:提升开发效率

原生 CSS 没有变量、复用、逻辑控制等能力,写复杂样式时容易出现 “重复代码多、修改繁琐、结构混乱” 的问题,SCSS 针对性解决了这些痛点:

1. 变量($ 定义):统一管理可复用值
  • 作用:将重复使用的样式值(如颜色、尺寸、间距)定义为变量,修改时只需改一处,避免全局查找替换。

  • 场景:项目主题色、侧边栏宽度、通用间距等。

  • 示例

    scss

    
    // 定义变量
    $primary-color: #324157; // 主色调
    $sidebar-width: 210px;    // 侧边栏宽度
    $gap: 16px;               // 通用间距
    
    // 使用变量
    .sidebar {
      width: $sidebar-width;
      background: $primary-color;
      padding: $gap;
    }
  • 好处:主题切换、全局样式调整时,无需逐行修改,降低出错概率。

2. 混合宏(@mixin):复用带逻辑的样式片段
  • 作用:将重复的样式块(甚至带参数的动态样式)封装为 “函数式片段”,需要时直接调用,支持传参自定义。

  • 场景:flex 居中、文字省略、滚动条样式等通用样式。

  • 示例

    scss

    
    // 定义带参数的混合宏(多行文字省略)
    @mixin text-ellipsis($line: 1) {
      overflow: hidden;
      text-overflow: ellipsis;
      @if $line == 1 {
        white-space: nowrap; // 单行
      } @else {
        display: -webkit-box;
        -webkit-line-clamp: $line; // 多行
        -webkit-box-orient: vertical;
      }
    }
    
    // 使用混合宏(2行省略)
    .card-title {
      @include text-ellipsis(2);
      font-size: 16px;
    }
  • 好处:减少重复代码,统一通用样式逻辑(如所有 “文字省略” 样式只需维护一个混合宏)。

3. 嵌套语法:贴合 HTML 结构,减少选择器冗余
  • 作用:支持样式选择器嵌套,结构与 HTML 保持一致,无需重复写父选择器,代码更易读。

  • 场景:组件内部的父子元素样式(如导航栏 → 菜单项 → 激活态)。

  • 示例

    scss

    
    // SCSS 嵌套(贴合 HTML 结构)
    .nav {
      height: 60px;
      background: #fff;
    
      // 子元素 .nav-item
      .nav-item {
        float: left;
        padding: 0 20px;
    
        // 子元素的伪类
        &:hover {
          color: #409EFF;
        }
    
        // 子元素的激活态
        &.active {
          border-bottom: 2px solid #409EFF;
        }
      }
    }
    
    // 编译后的原生 CSS(自动补全父选择器)
    .nav { height: 60px; background: #fff; }
    .nav .nav-item { float: left; padding: 0 20px; }
    .nav .nav-item:hover { color: #409EFF; }
    .nav .nav-item.active { border-bottom: 2px solid #409EFF; }
  • 好处:避免选择器冗余(如不用写 .nav .nav-item 多次),结构更清晰,降低维护成本。

二、提升代码质量:可维护性与扩展性

1. 模块化(@use/@forward):拆分样式文件,解耦逻辑
  • 作用:将样式按功能拆分到不同文件(如 mixin.scss 放混合宏、variables.scss 放变量、sidebar.scss 放侧边栏样式),通过 @use 导入使用,避免单文件过大。

  • 场景:中大型项目(如后台管理系统)的样式拆分。

  • 示例

    scss

    
    // 1. 拆分变量文件:src/styles/variables.scss
    $primary-color: #324157;
    $sidebar-width: 210px;
    
    // 2. 拆分混合宏文件:src/styles/mixin.scss
    @use './variables' as var; // 导入变量文件
    @mixin sidebar-width {
      width: var.$sidebar-width; // 使用变量
    }
    
    // 3. 组件中使用:src/components/Sidebar.vue
    @use '@/styles/mixin' as mix; // 导入混合宏
    .sidebar {
      @include mix.sidebar-width;
      background: var.$primary-color;
    }
  • 好处:样式文件按功能划分,权责清晰,多人协作时不易冲突(如改变量不影响混合宏,改侧边栏样式不影响全局)。

2. 继承(@extend):复用相同样式,减少代码冗余
  • 作用:让一个选择器 “继承” 另一个选择器的所有样式,适合纯样式复用(无参数需求)。

  • 场景:多个元素共享基础样式(如 “成功按钮” 和 “主要按钮” 共享基础按钮样式)。

  • 示例

    scss

    
    // 基础按钮样式
    .btn {
      padding: 8px 16px;
      border: none;
      border-radius: 4px;
      cursor: pointer;
    }
    
    // 成功按钮继承基础样式,再扩展自己的颜色
    .btn-success {
      @extend .btn;
      background: #30B08F;
      color: #fff;
    }
    
    // 主要按钮继承基础样式,扩展自己的颜色
    .btn-primary {
      @extend .btn;
      background: #409EFF;
      color: #fff;
    }
  • 好处:避免重复写基础样式,编译后会合并选择器(如 .btn, .btn-success, .btn-primary { ... }),减少 CSS 文件体积。

3. 逻辑控制:支持条件、循环,实现动态样式
  • 作用:通过 @if/@else(条件)、@for/@each(循环)实现动态样式,适合批量生成或按条件切换的场景。

  • 场景:生成多尺寸的间距类、按主题切换样式。

  • 示例

    scss

    
    // 1. 条件判断:按主题切换颜色
    $theme: 'dark'; // 主题变量
    .header {
      @if $theme == 'dark' {
        background: #1f2d3d;
        color: #fff;
      } @else {
        background: #fff;
        color: #333;
      }
    }
    
    // 2. 循环生成:批量生成间距类(m-1 ~ m-4)
    @for $i from 1 through 4 {
      .m-#{$i} {
        margin: $i * 8px; // 每次递增 8px
      }
    }
  • 好处:复杂样式逻辑无需手写重复代码(如 10 个间距类只需一行循环),支持动态主题切换。

三、生态与工具链:无缝集成现代开发流程

1. 与构建工具兼容:Vite、Webpack 等自动处理
  • SCSS 可被主流构建工具(Vite、Webpack、Rollup)通过插件(如 sass-loader)自动编译为原生 CSS,无需手动处理。
  • 例如 Vite 中只需安装 sass 依赖,即可直接使用 .scss 文件,配置简单。
2. 支持后处理器:与 PostCSS 配合增强功能
  • SCSS 可与 PostCSS(自动前缀、CSS 压缩等)配合使用:先通过 SCSS 编译出 CSS,再通过 PostCSS 自动添加浏览器前缀(如 -webkit--moz-)、压缩代码,兼顾开发效率和浏览器兼容性。
3. 社区成熟:丰富的插件与预设
  • 有大量成熟的 SCSS 工具库(如 BEM 命名规范插件、Bootstrap 的 SCSS 源码),可直接复用,减少重复造轮子。
总结:SCSS 适合什么场景?
  • 中小项目:通过变量、混合宏减少重复代码,提升开发速度;
  • 中大型项目:通过模块化拆分、逻辑控制实现样式解耦,降低维护成本;
  • 团队协作:统一样式规范(如变量、混合宏),避免风格混乱。

简单来说,SCSS 不是 “替代 CSS”,而是 “增强 CSS”—— 让你用更高效、更优雅的方式写 CSS,尤其在项目复杂度提升时,其优势会越来越明显。

四、回到项目中来

我包装的mixin.scss如下:


// 清除浮动
@mixin clearfix {
  &:after {
    content: "";
    display: table;
    clear: both;
  }
}

// 给页面滚动条设置样式
@mixin scrollBar {
  &::-webkit-scrollbar-track-piece {
    background: #d3dce6;
  }

  &::-webkit-scrollbar {
    width: 6px;
  }

  &::-webkit-scrollbar-thumb {
    background: #99a9bf;
    border-radius: 20px;
  }
}

// 相对定位(父元素)
@mixin relative {
  position: relative;
  width: 100%;
  height: 100%;
}

// 快速设置 “指定宽度 + 居中”
@mixin pct($pct) {
  width: #{$pct};
  position: relative;
  margin: 0 auto;
}

// 快速弄个三角形出来
@mixin triangle($width, $height, $color, $direction) {
  $width: $width/2;
  $color-border-style: $height solid $color;
  $transparent-border-style: $width solid transparent;
  height: 0;
  width: 0;

  @if $direction==up {
    border-bottom: $color-border-style;
    border-left: $transparent-border-style;
    border-right: $transparent-border-style;
  }

  @else if $direction==right {
    border-left: $color-border-style;
    border-top: $transparent-border-style;
    border-bottom: $transparent-border-style;
  }

  @else if $direction==down {
    border-top: $color-border-style;
    border-left: $transparent-border-style;
    border-right: $transparent-border-style;
  }

  @else if $direction==left {
    border-right: $color-border-style;
    border-top: $transparent-border-style;
    border-bottom: $transparent-border-style;
  }
}

// 绝对定位居中(支持水平/垂直/全居中)
@mixin absoluteCenter($type: both) {
  position: absolute;
  @if $type == both { // 水平+垂直居中
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
  } @else if $type == horizontal { // 仅水平居中
    left: 50%;
    transform: translateX(-50%);
  } @else if $type == vertical { // 仅垂直居中
    top: 50%;
    transform: translateY(-50%);
  }
}

// 内容在容器中完全居中(水平+垂直居中,不换行)
@mixin flex-center() {
  display: flex;
  justify-content: center; // 主轴居中
  align-items: center;     // 交叉轴居中
  flex-wrap: nowrap;       // 不换行
}

// 内容左对齐,垂直居中(比如列表项、导航栏)
@mixin flex-start-center() {
  display: flex;
  justify-content: flex-start; // 主轴左对齐
  align-items: center;         // 交叉轴居中
  flex-wrap: nowrap;
}

// 内容右对齐,垂直居中(比如顶部导航的用户菜单)
@mixin flex-end-center() {
  display: flex;
  justify-content: flex-end; // 主轴右对齐
  align-items: center;       // 交叉轴居中
  flex-wrap: nowrap;
}

// 两端对齐(左右靠边,中间留白),垂直居中(比如卡片标题和操作按钮)
@mixin flex-between-center() {
  display: flex;
  justify-content: space-between; // 主轴两端对齐
  align-items: center;            // 交叉轴居中
  flex-wrap: nowrap;
}

// 水平居中,垂直靠上(比如多行文字顶部居中)
@mixin flex-center-start() {
  display: flex;
  justify-content: center;   // 主轴居中
  align-items: flex-start;   // 交叉轴居上
  flex-wrap: nowrap;
}

// 内容平均分布,垂直居中,超出换行(比如标签列表、图片网格)
@mixin flex-around-center-wrap() {
  display: flex;
  justify-content: space-around; // 主轴平均分布(两侧留白相等)
  align-items: center;           // 交叉轴居中
  flex-wrap: wrap;               // 换行
}

// 左对齐,子元素拉伸占满高度,超出换行(比如表单项目)
@mixin flex-start-stretch-wrap() {
  display: flex;
  justify-content: flex-start; // 主轴左对齐
  align-items: stretch;        // 交叉轴拉伸(子元素高度相等)
  flex-wrap: wrap;             // 换行
}

// 通用 flex 混入:可自定义主轴、交叉轴、换行方式
@mixin flex(
  $justify: center,    // 主轴默认左对齐
  $align: center,         // 交叉轴默认拉伸
  $wrap: nowrap            // 默认不换行
) {
  display: flex;
  justify-content: $justify;
  align-items: $align;
  flex-wrap: $wrap;
}

// 文字省略:单行或多行
@mixin textEllipsis($line: 1) {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap; // 单行不换行
  
  @if $line > 1 { // 多行省略(需浏览器支持)
    white-space: normal;
    display: -webkit-box;
    -webkit-line-clamp: $line; // 显示行数
    -webkit-box-orient: vertical;
  }
}

我包装的variables.scss如下:


// -------------------------- 基础颜色变量 --------------------------
// 深蓝色(常用于主色调、标题等)
$blue: #324157;
// 浅蓝色(常用于次要强调、边框等)
$light-blue: #3A71A8;
// 红色(常用于错误提示、删除按钮等)
$red: #C03639;
// 粉红色(常用于特殊标记、提醒等)
$pink: #E65D6E;
// 绿色(常用于成功提示、确认按钮等)
$green: #30B08F;
// 蒂芙尼蓝(常用于特殊模块、高亮等)
$tiffany: #4AB7BD;
// 黄色(常用于警告提示、重要标记等)
$yellow: #FEC171;
// 潘通绿(和上面的绿色类似,可能用于特定场景的区分)
$panGreen: #30B08F;


// -------------------------- 侧边栏专用变量 --------------------------
// 侧边栏菜单文字默认颜色
$menuText: #bfcbd9;
// 侧边栏菜单选中时的文字颜色
$menuActiveText: #409EFF;
// 侧边栏子菜单选中时的文字颜色
$subMenuActiveText: #f4f4f5;

// 侧边栏菜单背景色
$menuBg: #304156;
// 侧边栏菜单项 hover(鼠标经过)时的背景色
$menuHover: #263445;

// 侧边栏子菜单背景色
$subMenuBg: #1f2d3d;
// 侧边栏子菜单项 hover(鼠标经过)时的背景色
$subMenuHover: #001528;

// 侧边栏宽度(用于布局计算,比如内容区宽度 = 100% - 侧边栏宽度)
$sideBarWidth: 210px;


// -------------------------- 供 JS 访问的变量 --------------------------
// 作用:让 JS 能读取 SCSS 中的变量(比如在 Vue 组件的 JS 部分动态计算布局时使用)
:root {
  --menuText: #{$menuText};           // 导出菜单文字颜色
  --menuActiveText: #{$menuActiveText}; // 导出菜单选中文字颜色
  --subMenuActiveText: #{$subMenuActiveText}; // 导出子菜单选中文字颜色
  --menuBg: #{$menuBg};               // 导出菜单背景色
  --menuHover: #{$menuHover};         // 导出菜单 hover 背景色
  --subMenuBg: #{$subMenuBg};         // 导出子菜单背景色
  --subMenuHover: #{$subMenuHover};   // 导出子菜单 hover 背景色
  --sideBarWidth: #{$sideBarWidth};   // 导出侧边栏宽度
}

在main.js中导入全局变量,一次导入


import { createApp } from 'vue'
import { createPinia } from 'pinia'
// 导入vite-plugin-svg-icons生成的雪碧图注册脚本
// 注意:这个文件是Vite构建时自动生成的,无需手动创建
import 'virtual:svg-icons-register'
// 初始化css样式
import 'normalize.css'
// 全局变量css样式
import '@/styles/variables.scss'
import App from './App.vue'
import router from '@/router/index.js' // 引入路由配置

import SvgIcon from '@/components/SvgIcon/index.vue' // 导入组件

// 创建Pinia实例
const pinia = createPinia()

// 创建app实例
const app = createApp(App)
// 全局注册SvgIcon组件
app.component('SvgIcon', SvgIcon)
app.use(pinia)
app.use(router)

// 挂在实例
app.mount('#app')

在layout中使用mixin.scss

使用@include语法导入所需要的预定样式


<style lang="scss" scoped>
@use "@/styles/mixin.scss" as *;
.app-wrapper {
    @include clearfix;
    position: relative;
    height: 100%;
    width: 100%;

    &.mobile.openSidebar {
      position: fixed;
      top: 0;
    }
  }

  .drawer-bg {
    background: #000;
    opacity: 0.3;
    width: 100%;
    top: 0;
    height: 100%;
    position: absolute;
    z-index: 999;
  }

  .fixed-header {
    position: fixed;
    top: 0;
    right: 0;
    z-index: 9;
    width: calc(100% - var(--sideBarWidth));
    transition: width 0.28s;
  }

  .hideSidebar .fixed-header {
    width: calc(100% - 54px)
  }

  .mobile .fixed-header {
    width: 100%;
  }

</style>