七、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>