SCSS(Sass)作为前端最主流的CSS预处理器,凭借变量、混合器、嵌套等强大扩展特性,彻底解决了原生CSS可维护性差、复用性低、逻辑冗余等痛点,成为前端开发的必备工具。
🌟**【青柠代码录】--- 青柠来相伴,代码更简单** 🌟
🔥**【全栈】博客合集:** https://www.yuque.com/u12587869/zplytb/ur5ohwqxd2axtiny 🔥
🎯**【Java】面试题:** https://www.yuque.com/u12587869/zplytb/eh7yqzitiab693og 🎯
1. 为何选择SCSS
在前端开发中,原生CSS的局限性(无变量、复用性差、嵌套混乱)导致样式管理成为一大挑战。
SCSS作为CSS的超集,不仅兼容所有CSS语法,还引入了多项实用特性,简化了样式开发与维护工作。
1.1 SCSS的核心优势
-
高效复用:通过变量、混合器、继承等特性,可以高效地重复利用样式逻辑。
-
提高可读性:嵌套语法符合HTML DOM结构,提升了代码的可读性和可维护性,特别适用于复杂组件的样式设计。
-
扩展性强:支持条件判断、循环和自定义函数,满足复杂样式逻辑的需求。
-
良好的生态:无缝集成Vue3、Vite、React等主流技术栈,并支持Element Plus、Ant Design Vue等组件库的样式定制。
1.2 SCSS与Vue3 + Vite的完美结合
-
Vite对SCSS的支持 :安装
sass依赖后,Vite能自动编译SCSS文件,支持全局变量注入等功能。 -
Vue3与SCSS的协作 :Vue3单文件组件中使用SCSS时,可通过
scoped属性实现样式隔离,并支持动态控制样式变化。 -
协同优势:三者结合,能够实现快速开发、构建优化及热更新,是现代前端项目的理想选择。
2. 环境搭建
Vite对SCSS的原生支持的,无需复杂配置,只需完成"初始化Vue3 + Vite项目→安装SCSS依赖→验证集成"三步,即可快速实现SCSS落地。
2.1 步骤1:初始化Vue3 + Vite项目
打开终端,执行以下命令,初始化标准Vue3 + Vite项目(推荐TypeScript,提升代码可维护性,JavaScript步骤完全一致):
# 初始化Vue3 + Vite项目(按需勾选配置,推荐选项如下)
npm create vue@latest vue3-vite-scss-demo
# 进入项目目录
cd vue3-vite-scss-demo
# 安装项目依赖(Vite项目依赖安装速度极快)
npm install
初始化配置建议(勾选选项参考,重点适配SCSS开发):
-
Project name:vue3-vite-scss-demo(自定义,建议包含scss标识,便于区分技术栈)
-
Package name:默认即可,与项目名称一致
-
Add TypeScript?:Yes(项目首选,提升代码健壮性,便于SCSS与逻辑协同)
-
Add JSX Support?:No(非必要不勾选,避免冗余)
-
Add Vue Router?:Yes(单页应用必备,不影响SCSS使用)
-
Add Pinia?:Yes(Vue3官方状态管理,后续可实现SCSS变量与响应式协同)
-
Add Vitest?:No(按需选择,小型项目可暂不勾选)
-
Add ESLint?:Yes(代码规范必备,避免多人协作时SCSS样式混乱)
-
Add Prettier?:Yes(代码格式化,统一SCSS代码风格)
-
Add production-ready starter template?:No(自定义配置更灵活,贴合SCSS工程化需求)
2.2 步骤2:安装SCSS依赖
Vite编译SCSS需依赖sass(即dart-sass,官方推荐),无需安装其他loader,dart-sass比node-sass更稳定、更快,无系统兼容性问题(开发已全面淘汰node-sass),执行以下命令安装开发依赖:
# 安装sass(开发依赖,仅开发时使用,生产环境会编译为CSS)
npm install -D sass
2.3 步骤3:验证SCSS集成成功
修改入口组件App.vue,在<style>标签添加lang="scss",书写SCSS核心语法(变量、嵌套、运算),测试是否能正常编译,步骤如下:
<template>
<div class="app">
<h1 class="app-title">SCSS集成测试()</h1>
<div class="demo-card">
<p class="demo-desc">变量、嵌套、运算语法验证</p>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const msg = ref('SCSS集成成功!')
</script>
<style lang="scss">
// SCSS变量(组件内局部变量,后续讲解全局变量)
$primary-color: #42b983; // 常用主色调(Element Plus同款)
$font-size-base: 14px; // 基础字体大小
$card-padding: 20px; // 卡片内边距
$border-radius: 8px; // 全局圆角
// SCSS嵌套语法(贴合HTML结构,减少父选择器重复书写)
.app {
width: 100%;
min-height: 100vh;
padding: 40px;
box-sizing: border-box;
background-color: #f8f9fa;
// 嵌套子选择器
.app-title {
color: $primary-color;
font-size: $font-size-base * 1.6; // SCSS运算(动态计算)
font-weight: 600;
margin-bottom: 30px;
}
// 嵌套卡片样式
.demo-card {
background-color: #fff;
padding: $card-padding;
border-radius: $border-radius;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
.demo-desc {
color: #666;
font-size: $font-size-base;
line-height: 1.5;
}
}
}
</style>
执行启动命令,验证效果:
# 启动开发服务器(Vite热更新开启,修改SCSS无需重启)
npm run dev
访问终端输出的地址(默认http://localhost:5173),若满足以下条件,说明SCSS集成成功:
-
标题颜色为$primary-color定义的绿色(#42b983)
-
卡片有圆角、阴影,内边距符合$card-padding定义
-
标题字体大小为14px * 1.6 = 22.4px(SCSS运算生效)
3. SCSS核心语法
SCSS完全兼容原生CSS,开发中无需放弃原有CSS知识,重点掌握「变量、嵌套、混合器、继承、内置函数」五大核心特性,即可大幅提升样式开发效率。
3.1 变量(Variables)
SCSS变量使用$开头,用于存储颜色、字体、间距、尺寸等样式常量,支持全局与局部作用域,是开发中"样式统一、可维护"的关键,也是多人协作避免样式混乱的核心。
3.1.1 变量的定义与使用(基础实战)
开发中,变量通常用于统一设计规范(品牌色、字体、间距),示例:Vue3组件中用变量统一按钮样式,后续修改只需调整变量,无需逐个修改组件:
<template>
<div class="button-container">
<button class="primary-btn">主要按钮</button>
<button class="secondary-btn">次要按钮</button>
<button class="disabled-btn">禁用按钮</button>
</div>
</template>
<script setup lang="ts">
const isDisabled = ref(true)
</script>
<style lang="scss">
// 组件内局部变量(仅当前组件生效)
$primary-btn-color: #42b983; // 品牌主色
$secondary-btn-color: #64748b; // 次要色
$disabled-btn-color: #c0ccda; // 禁用色
$btn-padding: 8px 16px; // 统一内边距
$btn-border-radius: 4px; // 统一圆角
$btn-hover-opacity: 0.9; // 统一交互效果
// 主要按钮样式(复用变量)
.primary-btn {
background-color: $primary-btn-color;
color: #fff;
padding: $btn-padding;
border-radius: $btn-border-radius;
border: none;
cursor: pointer;
transition: opacity 0.2s;
&:hover {
opacity: $btn-hover-opacity;
}
}
// 次要按钮样式(复用变量,减少冗余)
.secondary-btn {
background-color: $secondary-btn-color;
color: #fff;
padding: $btn-padding;
border-radius: $btn-border-radius;
border: none;
cursor: pointer;
margin: 0 10px;
transition: opacity 0.2s;
&:hover {
opacity: $btn-hover-opacity;
}
}
// 禁用按钮样式(复用变量)
.disabled-btn {
background-color: $disabled-btn-color;
color: #fff;
padding: $btn-padding;
border-radius: $btn-border-radius;
border: none;
cursor: not-allowed;
opacity: 0.7;
}
</style>
应用场景:项目中所有按钮、卡片、表单的样式,均通过变量统一管理,后续若需修改品牌色、圆角大小,只需调整对应变量,无需逐个修改组件,维护成本大幅降低。
3.1.2 变量的作用域(避免样式污染)
SCSS变量分为「局部变量」和「全局变量」,开发中需严格区分,避免样式污染,核心规则如下:
-
局部变量:定义在选择器、混合器、函数内部,仅当前作用域生效,适合组件内专属样式,不影响外部。
-
全局变量:定义在所有选择器外部,或使用!global关键字声明,可在整个项目的SCSS文件、组件中生效,适合全局设计规范(品牌色、基础字体)。
示例:全局变量与局部变量的区别:
// 全局变量(组件样式最外层,当前组件所有选择器可访问)
$global-color: #333; // 全局文本色
$global-padding: 20px; // 全局基础内边距
.app {
// 局部变量(仅.app内部生效,外部无法访问)
$local-bg-color: #f5f5f5;
background-color: $local-bg-color;
padding: $global-padding; // 复用全局变量
.app-title {
color: $global-color; // 访问全局变量
}
.app-content {
// 嵌套作用域局部变量(仅.app-content内部生效)
$content-color: #666;
color: $content-color;
padding: $global-padding / 2; // SCSS运算
}
}
// 外部选择器(与.app同级)
.other-container {
color: $global-color; // 可访问全局变量
// background-color: $local-bg-color; // 报错:局部变量无法访问
}
// 慎用!global:将局部变量强制转为全局(易污染,开发尽量避免)
.box {
$box-color: #999 !global;
color: $box-color;
}
.another-box {
color: $box-color; // 可访问(已转为全局)
}
SCSS开发规范:避免使用!global声明全局变量,建议将全局变量集中管理在单独的SCSS文件中(后续4.1节讲解),通过Vite全局注入,既保证全局可用,又避免样式污染。
3.1.3 变量默认值(组件封装必备)
使用!default关键字为变量设置默认值,当变量未被定义时使用默认值;若已定义,则不覆盖原有值,这是SCSS组件封装的核心技巧,允许使用者自定义样式,同时保留默认样式。
<template>
<div class="custom-card">
<slot>默认卡片内容</slot>
</div>
</template>
<style lang="scss">
// 变量默认值(!default关键字)
$card-bg-color: #fff !default; // 默认背景色
$card-border-radius: 8px !default; // 默认圆角
$card-padding: 20px !default; // 默认内边距
.custom-card {
background-color: $card-bg-color;
border-radius: $card-border-radius;
padding: $card-padding;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
}
</style>
应用场景:封装通用卡片组件时,通过!default定义变量,其他页面使用该组件时,可重新定义变量覆盖默认样式,无需修改组件源码,实现组件的灵活性与复用性。
3.2 嵌套(Nesting):贴合结构,减少冗余
嵌套语法是SCSS最常用的特性,核心优势是「贴合HTML DOM结构」,无需重复书写父选择器,让样式代码更简洁、可读性更强,尤其适合复杂组件(表单、弹窗)的样式开发,同时避免选择器冲突。
3.2.1 基础嵌套(父子选择器)
SCSS允许在父选择器内部嵌套子选择器,子选择器自动继承父选择器作用域,相当于原生CSS的"父选择器+子选择器",示例(Vue3表单组件样式):
<template>
<form class="login-form">
<div class="form-item">
<label class="form-label">用户名</label>
<input type="text" class="form-input" placeholder="请输入用户名">
</div>
<div class="form-item">
<label class="form-label">密码</label>
<input type="password" class="form-input" placeholder="请输入密码">
</div>
<button class="form-btn" type="submit">登录</button>
</form>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const username = ref('')
const password = ref('')
const handleSubmit = (e: Event) => {
e.preventDefault()
console.log('提交表单:', username.value, password.value)
}
</script>
<style lang="scss">
// 父选择器:表单容器
.login-form {
width: 350px;
margin: 50px auto;
padding: 30px;
background-color: #fff;
border-radius: $border-radius;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
// 嵌套子选择器:表单项(.login-form .form-item)
.form-item {
margin-bottom: 20px;
position: relative;
// 嵌套孙选择器:表单标签(.login-form .form-item .form-label)
.form-label {
display: block;
margin-bottom: 8px;
color: #333;
font-size: $font-size-base;
font-weight: 500;
}
// 嵌套孙选择器:表单输入框
.form-input {
width: 100%;
height: 40px;
padding: 0 12px;
border: 1px solid #e5e6eb;
border-radius: 4px;
font-size: $font-size-base;
box-sizing: border-box;
// 伪类嵌套(&表示父选择器本身)
&:focus {
outline: none;
border-color: $primary-color;
box-shadow: 0 0 0 2px rgba(66, 185, 131, 0.2);
}
&:disabled {
background-color: #f5f5f5;
cursor: not-allowed;
border-color: #c0ccda;
}
}
}
// 嵌套子选择器:登录按钮
.form-btn {
width: 100%;
height: 44px;
background-color: $primary-color;
color: #fff;
border: none;
border-radius: 4px;
font-size: $font-size-base;
cursor: pointer;
transition: background-color 0.2s;
&:hover {
background-color: darken($primary-color, 10%); // SCSS内置函数
}
&:disabled {
background-color: #a0e5c8;
cursor: not-allowed;
}
}
}
</style>
SCSS开发重点:嵌套层级不可超过3层,否则会导致编译后的CSS选择器过长,影响性能,同时降低可读性。可通过BEM命名规范减少嵌套层级,避免过度嵌套。
3.2.2 特殊嵌套:&符号的使用(必备技巧)
在SCSS嵌套中,&符号代表「父选择器本身」,主要用于伪类、伪元素、相邻选择器等场景,避免重复书写父选择器,是开发中SCSS嵌套的核心技巧:
// 示例1:伪类嵌套(最常用)
.btn {
background-color: $primary-color;
color: #fff;
// &代表.btn
&:hover { background-color: darken($primary-color, 8%); }
&:active { background-color: darken($primary-color, 15%); }
&:disabled { background-color: #c0ccda; cursor: not-allowed; }
}
// 示例2:伪元素嵌套
.card {
position: relative;
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 4px;
background-color: $primary-color;
}
&::after {
content: '';
position: absolute;
bottom: 0;
right: 0;
width: 40px;
height: 40px;
background: url('@/assets/icon.png') no-repeat center;
}
}
// 示例3:组件样式批量生成(组件封装常用)
.btn {
// 基础样式
&-primary { @include btn-style($primary-color); }
&-secondary { @include btn-style($secondary-color); }
&-danger { @include btn-style($danger-color); }
}
// 编译后生成:.btn-primary、.btn-secondary、.btn-danger
应用场景:封装通用按钮、卡片组件时,使用&-xxx语法可快速生成不同类型的组件样式,无需重复书写基础样式,提升组件封装效率。
3.3 混合器(Mixin):复杂样式逻辑复用核心
混合器(@mixin)相当于CSS中的"函数",用于封装一段可复用的复杂样式逻辑,支持传递参数、设置默认值,是开发中重复样式复用的核心,适用于圆角、阴影、弹性布局等常用复杂样式。
3.3.1 无参数Mixin(简单样式复用)
无参数Mixin用于封装固定样式逻辑,可直接引用,示例(封装卡片基础样式):
<template>
<div class="card-container">
<div class="card card1">卡片1(基础样式)</div>
<div class="card card2">卡片2(基础样式+额外样式)</div>
</div>
</template>
<style lang="scss">
// 定义无参数Mixin(@mixin关键字)
@mixin card-base {
background-color: #fff;
border-radius: $border-radius;
padding: 20px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
box-sizing: border-box;
transition: box-shadow 0.2s;
&:hover {
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
}
}
// 引用Mixin(@include关键字)
.card-container {
display: flex;
gap: 20px;
padding: 20px;
}
.card {
width: 200px;
@include card-base; // 引用卡片基础样式
// 额外样式补充
&.card1 { border-left: 4px solid $primary-color; }
&.card2 { border-left: 4px solid #1890ff; }
}
</style>
应用场景:项目中所有卡片组件(列表卡片、详情卡片)均引用card-base混合器,统一基础样式,再添加个性化样式,实现"基础复用、灵活扩展"。
3.3.2 带参数Mixin(灵活复用)
带参数Mixin允许传递参数、设置默认值,适配不同场景,是开发中SCSS混合器的高频用法,示例(封装弹性布局、圆角、阴影等常用混合器):
// 1. 弹性布局混合器(适配不同布局场景)
@mixin flex-layout($justify: center, $align: center, $direction: row, $gap: 0) {
display: flex;
justify-content: $justify;
align-items: $align;
flex-direction: $direction;
gap: $gap;
}
// 2. 圆角混合器(可自定义圆角大小)
@mixin border-radius($radius: $border-radius) {
border-radius: $radius;
-webkit-border-radius: $radius; // 兼容旧浏览器
}
// 3. 阴影混合器(可自定义阴影深度)
@mixin box-shadow($depth: 1) {
@if $depth == 1 { box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); }
@else if $depth == 2 { box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12); }
@else if $depth == 3 { box-shadow: 0 8px 32px rgba(0, 0, 0, 0.16); }
}
// 引用带参数Mixin(传递不同参数,实现不同样式)
.header {
@include flex-layout($justify: space-between, $align: center, $gap: 16px);
padding: 0 20px;
height: 60px;
@include border-radius(0); // 取消圆角
@include box-shadow(1);
}
.footer {
@include flex-layout($justify: center, $align: center, $direction: column, $gap: 8px);
padding: 20px;
@include border-radius(8px);
@include box-shadow(0); // 无阴影
}
SCSS开发重点:带参数Mixin的核心价值是"一次封装,多场景复用",例如flex-layout混合器可适配头部、底部、卡片等不同组件的布局需求,无需重复书写flex相关样式,降低维护成本。
3.4 继承(Extend):简单样式规则复用
继承(@extend)用于复用某个选择器的全部样式规则,与Mixin的区别在于:Mixin是"复制样式代码",继承是"合并选择器",可减少编译后的CSS冗余,适合复用简单样式规则(文本样式、基础状态)。
示例(Vue3组件中使用继承,复用文本样式):
<template>
<div class="text-container">
<p class="text-primary">主要文本(加粗、主色调)</p>
<p class="text-secondary">次要文本(常规、灰色)</p>
<p class="text-primary text-large">主要大文本(加粗、主色调、大字体)</p>
</div>
</template>
<style lang="scss">
// 基础文本样式(被继承的样式)
.text-base {
font-size: $font-size-base;
line-height: 1.5;
margin: 8px 0;
}
// 继承基础文本样式,并添加额外样式
.text-primary {
@extend .text-base; // 继承.text-base所有样式
color: $primary-color;
font-weight: 600;
}
.text-secondary {
@extend .text-base; // 继承.text-base所有样式
color: #666;
font-weight: 400;
}
// 继承+额外样式(叠加效果)
.text-large {
font-size: $font-size-base * 1.2;
}
</style>
SCSS开发注意事项:继承仅适用于简单样式复用,复杂样式逻辑(带参数、多层嵌套)建议使用Mixin;避免继承嵌套选择器,防止样式污染;多个选择器复用同一简单样式时,继承比Mixin更节省CSS体积。
3.5 SCSS内置函数(实用工具)
SCSS内置大量实用函数,用于颜色处理、数学运算等,开发中高频使用的函数如下:
3.5.1 颜色处理函数(最常用)
// 1. darken($color, $percentage):加深颜色(百分比0-100%)
$primary-color: #42b983;
$primary-dark: darken($primary-color, 10%); // #359469
$primary-darker: darken($primary-color, 20%); // #286e4d
// 2. lighten($color, $percentage):变浅颜色
$primary-light: lighten($primary-color, 10%); // #5fc39c
// 3. rgba($color, $alpha):设置透明度(alpha 0-1)
$primary-opacity-50: rgba($primary-color, 0.5); // 半透明主色调
// 4. mix($color1, $color2, $percentage):混合两种颜色
$mix-color: mix($primary-color, #fff, 30%); // 30%主色+70%白色
// 应用示例(按钮交互样式)
.btn {
background-color: $primary-color;
&:hover { background-color: $primary-dark; } // 加深颜色,提升交互反馈
}
3.5.2 数学运算函数(响应式适配)
// 1. 基础运算(+、-、*、/),自动处理单位
$container-width: 1200px;
$sidebar-width: 240px;
$content-width: $container-width - $sidebar-width; // 960px
$font-size-base: 14px;
$font-size-large: $font-size-base * 1.2; // 16.8px
// 2. 内置数学函数
$max-width: max(1200px, 100vw); // 取最大值(适配移动端)
$round-size: round(16.8px); // 四舍五入(17px)
// 响应式布局应用
.container {
width: $max-width;
margin: 0 auto;
}
.content {
width: $content-width;
padding: $font-size-base * 1.5; // 动态计算内边距
}
应用场景:响应式布局中,通过数学运算函数动态计算尺寸;颜色处理函数用于生成hover、active等交互状态的颜色,保持颜色统一性。
4. SCSS工程化配置(模块化、全局注入)
项目中,SCSS的使用需实现「全局资源共享、模块化管理、样式规范统一」,核心是SCSS文件模块化拆分与Vite全局注入配置,解决多人协作中的样式混乱、重复引入等问题,提升开发效率。
4.1 第一步:SCSS模块化拆分
将SCSS资源按功能拆分,集中管理,避免样式混乱,推荐目录结构:
src/
├── styles/ # SCSS全局样式目录(核心)
│ ├── _variables.scss # 全局变量(颜色、字体、间距等)
│ ├── _mixins.scss # 全局混合器(布局、圆角、阴影等)
│ ├── _extends.scss # 全局继承样式(基础文本、按钮等)
│ ├── _functions.scss # 全局自定义函数(按需扩展)
│ └── main.scss # 全局样式入口(引入所有模块化文件)
├── components/ # 组件目录(组件内SCSS样式)
├── views/ # 页面目录(页面内SCSS样式)
└── App.vue # 入口组件
目录说明(SCSS核心):
-
_variables.scss:存储全局变量,是样式规范的核心,所有组件、页面均使用这里的变量,确保样式统一。
-
_mixins.scss:存储全局混合器,封装项目中常用的复杂样式逻辑,供所有组件、页面引用。
-
_extends.scss:存储全局继承样式,封装简单基础样式,减少重复代码。
-
_functions.scss:自定义SCSS函数,按需扩展SCSS功能。
-
main.scss:全局样式入口,引入所有模块化SCSS文件,同时书写全局公共样式(重置样式、body样式)。
SCSS模块化注意 :模块化文件建议以开头(如variables.scss),表示"局部文件",不会被单独编译为CSS,需通过@import引入后生效,避免生成冗余CSS。
4.1.1 模块化文件实战示例
示例1:_variables.scss(全局变量,SCSS规范核心)
// 一、品牌色(设计规范统一)
$primary-color: #42b983; // 主色调
$primary-dark: darken($primary-color, 10%); // 主色调加深
$primary-light: lighten($primary-color, 10%); // 主色调变浅
$secondary-color: #64748b; // 次要色调
$danger-color: #f56c6c; // 危险色
$success-color: #67c23a; // 成功色
$text-color: #333; // 主要文本色
$bg-color: #f8f9fa; // 页面背景色
$border-color: #e5e6eb; // 边框色
// 二、尺寸规范
$border-radius: 8px; // 全局圆角
$border-radius-sm: 4px; // 小圆角
$shadow-depth-1: 0 2px 8px rgba(0, 0, 0, 0.08); // 浅阴影
// 三、间距规范(统一间距,避免混乱)
$spacing-xs: 4px;
$spacing-sm: 8px;
$spacing-md: 16px;
$spacing-lg: 24px;
// 四、字体规范
$font-size-base: 14px; // 基础字体大小
$font-weight-medium: 500; // 中等加粗
// 五、组件尺寸规范
$btn-height-base: 40px; // 基础按钮高度
$card-padding-base: 20px; // 基础卡片内边距
示例2:_mixins.scss(全局混合器,常用)
// 引入全局变量(混合器中需使用变量)
@import './_variables.scss';
// 1. 弹性布局混合器
@mixin flex-layout($justify: center, $align: center, $direction: row, $gap: 0) {
display: flex;
justify-content: $justify;
align-items: $align;
flex-direction: $direction;
gap: $gap;
flex-wrap: wrap; // 默认换行,适配响应式
}
// 2. 圆角混合器
@mixin border-radius($radius: $border-radius) {
border-radius: $radius;
-webkit-border-radius: $radius;
}
// 3. 按钮样式混合器(核心混合器)
@mixin btn-style($color: $primary-color, $height: $btn-height-base) {
height: $height;
padding: $spacing-sm $spacing-md;
font-size: $font-size-base;
border: none;
color: #fff;
background-color: $color;
@include border-radius($border-radius-sm);
cursor: pointer;
transition: background-color 0.2s;
&:hover {
background-color: darken($color, 8%);
}
&:disabled {
background-color: lighten($color, 30%);
cursor: not-allowed;
}
}
// 4. 卡片基础混合器
@mixin card-base($padding: $card-padding-base) {
background-color: #fff;
padding: $padding;
@include border-radius;
@include box-shadow(1);
box-sizing: border-box;
}
// 5. 响应式混合器(适配多端)
@mixin responsive($breakpoint) {
@if $breakpoint == 'mobile' {
@media (max-width: 767px) { @content; }
} @else if $breakpoint == 'pc' {
@media (min-width: 1200px) { @content; }
}
}
示例3:main.scss(全局样式入口)
// 引入所有模块化SCSS文件
@import './_variables.scss';
@import './_mixins.scss';
@import './_extends.scss';
// 全局重置样式(项目必备)
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
// 全局body样式(复用SCSS变量)
body {
font-family: 'Microsoft YaHei', 'PingFang SC', sans-serif;
font-size: $font-size-base;
color: $text-color;
background-color: $bg-color;
line-height: 1.5;
}
// 全局链接样式
a {
text-decoration: none;
color: $primary-color;
&:hover { color: $primary-dark; }
}
4.2 第二步:Vite配置SCSS全局注入
模块化拆分后,若每个组件都手动@import全局变量、混合器,会非常繁琐且易遗漏。Vite提供全局资源注入配置,可将全局SCSS文件自动注入所有Vue组件的SCSS样式中,无需手动引入。
4.2.1 Vite配置步骤(vite.config.ts)
在项目根目录的vite.config.ts中添加以下配置(无则新建),重点配置SCSS全局注入:
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import path from 'path';
export default defineConfig({
plugins: [vue()],
// SCSS 全局注入配置(核心)
css: {
preprocessorOptions: {
scss: {
// 自动注入全局变量和混合器,无需手动 @import
additionalData: `
@import "@/styles/_variables.scss";
@import "@/styles/_mixins.scss";
`,
},
},
},
// 路径别名配置(可选,简化 SCSS 文件引入路径)
resolve: {
alias: {
'@': path.resolve(__dirname, './src'), // 配置 @ 为 src 目录别名
},
},
});
配置重点说明:
-
additionalData :用于注入全局SCSS资源,这里注入了variables.scss和mixins.scss,后续所有Vue组件的<style lang="scss">中,可直接使用全局变量和混合器,无需手动@import。注意末尾的分号和换行,避免编译报错。
-
路径别名 :配置@为src目录别名,SCSS文件中可通过@import "@/styles/*variables.scss"引入,无需书写相对路径(如../styles/*variables.scss),减少路径错误。
-
配置生效:修改vite.config.ts后,需重启Vite服务(npm run dev),配置才能生效,否则全局注入不生效。
4.2.2 全局注入验证
配置完成后,新建一个Vue组件(如TestGlobal.vue),无需@import全局变量和混合器,直接使用,验证全局注入是否成功:
<template>
<div class="test-global">
<button class="test-btn">全局混合器测试</button>
<div class="test-card">全局变量测试</div>
</div>
</template>
<style lang="scss">
// 直接使用全局变量(无需@import _variables.scss)
.test-global {
padding: $spacing-lg; // 全局间距变量
background-color: $bg-color; // 全局背景色变量
}
// 直接使用全局混合器(无需@import _mixins.scss)
.test-btn {
@include btn-style($primary-color); // 全局按钮混合器
margin-bottom: $spacing-md;
}
.test-card {
@include card-base; // 全局卡片混合器
border-left: 4px solid $primary-color;
}
</style>
启动服务后,若组件正常渲染,按钮和卡片样式符合全局配置,说明SCSS全局注入成功,后续开发可直接复用全局资源,大幅提升开发效率。
4.3 第三步:SCSS工程化规范
项目中,多人协作开发SCSS时,需遵循统一规范,避免样式混乱、代码冗余,以下是常用的SCSS工程化规范:
-
命名规范:采用BEM命名规范(Block-Element-Modifier),避免选择器嵌套过深,例如.btn(Block)、.btn__icon(Element)、.btn--primary(Modifier),配合SCSS嵌套使用,兼顾可读性与可维护性。
-
变量规范:全局变量统一放在_variables.scss中,按"功能分类"(品牌色、尺寸、间距等)整理,变量名采用"+功能+描述"格式(如primary-color、spacing-md),避免无意义变量名(如color1、$size2)。
-
混合器规范:复杂样式逻辑(3行以上可复用样式)才封装为混合器,混合器名采用"功能+style"格式(如btn-style、flex-layout),带参数混合器需设置合理默认值,适配多场景复用。
-
嵌套规范:嵌套层级不超过3层,避免编译后CSS选择器过长;伪类、伪元素、相邻选择器使用&符号,提升代码简洁度。
-
注释规范 :模块化文件(*variables.scss、*mixins.scss)添加分类注释;复杂样式、特殊逻辑添加单行注释,说明用途,便于多人协作理解。
5. SCSS实战:开发可复用通用组件
结合前文讲解的SCSS核心语法与工程化配置,本节实战开发3个通用组件(按钮、卡片、表单)。
5.1 实战1:通用按钮组件(Button)
按钮是项目中最常用的组件,通过SCSS混合器、变量封装,实现多类型、可自定义的通用按钮,支持主要、次要、危险、禁用等状态,适配不同场景。
<template>
<button
class="btn"
:class="{
'btn--primary': type === 'primary',
'btn--secondary': type === 'secondary',
'btn--danger': type === 'danger',
'btn--disabled': disabled
}"
:disabled="disabled"
@click="handleClick"
>
<slot>按钮</slot>
</button>
</template>
<script setup lang="ts">
import { defineProps, emit } from 'vue';
// 组件props(支持自定义配置)
const props = defineProps({
type: {
type: String,
default: 'primary', // 默认主要按钮
validator: (value: string) => ['primary', 'secondary', 'danger'].includes(value)
},
disabled: {
type: Boolean,
default: false
},
size: {
type: String,
default: 'base',
validator: (value: string) => ['small', 'base', 'large'].includes(value)
}
});
const emit = emit('click');
const handleClick = () => {
if (!props.disabled) {
emit('click');
}
};
</script>
<style lang="scss">
// 基础按钮样式(复用全局混合器,自定义参数)
.btn {
@include btn-style($primary-color, $btn-height-base);
padding: $spacing-sm $spacing-md;
font-size: $font-size-base;
// 尺寸适配(通过SCSS条件判断)
@if $size == 'small' {
height: $btn-height-base - 8px;
padding: $spacing-xs $spacing-sm;
font-size: $font-size-base - 2px;
} @else if $size == 'large' {
height: $btn-height-base + 8px;
padding: $spacing-md $spacing-lg;
font-size: $font-size-base + 2px;
}
// 不同类型按钮样式(复用全局变量)
&--primary { background-color: $primary-color; }
&--secondary { background-color: $secondary-color; }
&--danger { background-color: $danger-color; }
// 禁用状态(覆盖混合器默认禁用样式)
&--disabled {
background-color: $border-color;
color: #999;
cursor: not-allowed;
&:hover { background-color: $border-color; }
}
}
</style>
组件优势:通过全局混合器btn-style复用基础样式,结合props实现类型、尺寸、禁用状态的自定义,无需重复书写样式;全局变量修改后,按钮样式自动同步,维护成本极低,符合组件封装规范。
5.2 实战2:通用卡片组件(Card)
卡片组件用于展示内容,通过SCSS变量、混合器封装,支持自定义标题、边框、阴影、内边距,适配列表、详情等多种场景,兼顾美观与复用性。
<template>
<div class="card" :class="{ 'card--border': border, 'card--no-shadow': noShadow }">
<div class="card__header" v-if="title">
<h3 class="card__title">{{ title }}</h3>
</div>
<div class="card__body">
<slot>卡片内容</slot>
</div>
</div>
</template>
<script setup lang="ts">
import { defineProps } from 'vue';
const props = defineProps({
title: { type: String, default: '' }, // 卡片标题
border: { type: Boolean, default: false }, // 是否显示边框
noShadow: { type: Boolean, default: false }, // 是否取消阴影
padding: { type: String, default: $card-padding-base } // 自定义内边距(SCSS变量默认值)
});
</script>
<style lang="scss">
// 基础卡片样式(复用全局混合器)
.card {
@include card-base($padding); // 复用全局卡片混合器,支持自定义内边距
margin-bottom: $spacing-md;
// 卡片头部
&__header {
margin-bottom: $spacing-sm;
padding-bottom: $spacing-sm;
border-bottom: 1px solid $border-color;
}
// 卡片标题
&__title {
font-size: $font-size-base + 2px;
color: $text-color;
font-weight: $font-weight-medium;
margin: 0;
}
// 卡片内容
&__body {
color: #666;
line-height: 1.6;
}
// 边框样式(自定义状态)
&--border {
border: 1px solid $border-color;
}
// 取消阴影(自定义状态)
&--no-shadow {
box-shadow: none;
}
}
</style>
应用场景:列表页卡片、详情页卡片、数据统计卡片等,通过props自定义配置,无需重复开发,同时保持样式统一,符合设计规范。
5.3 实战3:通用表单组件(FormItem)
表单是项目中核心组件,封装FormItem组件,结合SCSS嵌套、变量,实现表单标签、输入框、提示信息的统一样式,支持必填、禁用、错误提示等状态。
<template>
<div class="form-item">
<label class="form-item__label" :class="{ 'form-item__label--required': required }">
{{ label }}
<span class="required-mark" v-if="required">*</span>
</label>
<div class="form-item__content">
<slot><!-- 插槽:输入框、下拉框等表单元素 --></slot>
<p class="form-item__error" v-if="error">{{ error }}</p>
</div>
</div>
</template>
<script setup lang="ts">
import { defineProps } from 'vue';
const props = defineProps({
label: { type: String, required: true }, // 表单标签
required: { type: Boolean, default: false }, // 是否必填
error: { type: String, default: '' }, // 错误提示
disabled: { type: Boolean, default: false } // 是否禁用
});
</script>
<style lang="scss">
// 表单项基础样式(SCSS嵌套)
.form-item {
margin-bottom: $spacing-md;
position: relative;
// 表单标签
&__label {
display: block;
margin-bottom: $spacing-xs;
color: $text-color;
font-size: $font-size-base;
font-weight: 500;
// 必填标记
.required-mark {
color: $danger-color;
margin-left: 2px;
}
// 禁用状态标签
&--required {
font-weight: 600;
}
}
// 表单内容容器
&__content {
position: relative;
// 错误提示
&__error {
position: absolute;
bottom: -18px;
left: 0;
color: $danger-color;
font-size: $font-size-base - 2px;
margin: 0;
line-height: 1.4;
}
}
// 禁用状态(适配插槽内的表单元素)
&:has(input:disabled),
&:has(select:disabled) {
.form-item__label {
color: #999;
}
}
}
</style>
组件优势:通过SCSS嵌套贴合表单结构,复用全局变量统一样式,支持必填、错误提示等常用状态,插槽设计适配输入框、下拉框等多种表单元素,灵活性高,可直接复用在登录、注册、表单提交等场景。