Appearance
Sass 混合宏与继承
Sass的混合宏(Mixins)和继承(Extend)是两个强大的功能,它们允许您创建可重用的样式代码块。本章将详细介绍这两种功能的使用方法、区别和最佳实践。
混合宏 (Mixins)
混合宏允许您定义可重用的样式块,可以接受参数并包含复杂的逻辑。
基本混合宏
scss
// 定义一个简单的混合宏
@mixin border-radius($radius) {
-webkit-border-radius: $radius;
-moz-border-radius: $radius;
-ms-border-radius: $radius;
border-radius: $radius;
}
// 使用混合宏
.box {
@include border-radius(10px);
}
// 定义带多个参数的混合宏
@mixin button-style($bg-color, $text-color, $padding: 10px 20px) {
background-color: $bg-color;
color: $text-color;
padding: $padding;
border: none;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.3s ease;
&:hover {
background-color: darken($bg-color, 10%);
}
}
// 使用混合宏
.primary-btn {
@include button-style(#007bff, white, 12px 24px);
}
.secondary-btn {
@include button-style(#6c757d, white);
}
带默认值的参数
scss
@mixin shadow($x: 0, $y: 0, $blur: 5px, $spread: 0, $color: rgba(0,0,0,0.1)) {
-webkit-box-shadow: $x $y $blur $spread $color;
-moz-box-shadow: $x $y $blur $spread $color;
box-shadow: $x $y $blur $spread $color;
}
// 使用默认值
.card {
@include shadow;
}
// 覆盖部分默认值
.featured-card {
@include shadow(2px, 4px, 8px, 0, rgba(0,0,0,0.15));
}
// 覆盖所有值
notification {
@include shadow(0, 2px, 10px, 2px, rgba(255,0,0,0.3));
}
可变参数 (Variable Arguments)
使用...来接受可变数量的参数:
scss
// 接受多个颜色参数
@mixin gradient($direction, $colors...) {
background: nth($colors, 1);
background: -webkit-linear-gradient($direction, $colors);
background: -moz-linear-gradient($direction, $colors);
background: -o-linear-gradient($direction, $colors);
background: linear-gradient($direction, $colors);
}
// 使用可变参数
.header {
@include gradient(to right, #ff0000, #00ff00, #0000ff);
}
// 接受多个盒子阴影
@mixin multi-shadow($shadows...) {
@if length($shadows) > 0 {
box-shadow: $shadows;
}
}
.element {
@include multi-shadow(
0 1px 3px rgba(0,0,0,0.12),
0 1px 2px rgba(0,0,0,0.24),
inset 0 1px 0 rgba(255,255,255,0.2)
);
}
@content 指令
允许在混合宏中插入额外内容:
scss
@mixin media-query($device) {
@if $device == mobile {
@media screen and (max-width: 768px) { @content; }
}
@else if $device == tablet {
@media screen and (min-width: 769px) and (max-width: 1024px) { @content; }
}
@else if $device == desktop {
@media screen and (min-width: 1025px) { @content; }
}
}
// 使用@content
.sidebar {
width: 300px;
@include media-query(mobile) {
width: 100%;
margin-bottom: 1rem;
}
@include media-query(tablet) {
width: 250px;
}
}
// 高级@content用法:创建包装器
@mixin wrapper($class-name) {
.#{$class-name} {
@content;
}
}
@include wrapper(alert) {
padding: 1rem;
border: 1px solid #ccc;
border-radius: 4px;
&.success {
border-color: #28a745;
background-color: #d4edda;
}
&.error {
border-color: #dc3545;
background-color: #f8d7da;
}
}
条件混合宏
scss
@mixin theme-style($theme) {
@if $theme == light {
background-color: white;
color: #333;
border: 1px solid #ddd;
}
@else if $theme == dark {
background-color: #333;
color: white;
border: 1px solid #555;
}
@else if $theme == primary {
background-color: #007bff;
color: white;
border: 1px solid #0062cc;
}
@else {
background-color: #f8f9fa;
color: #666;
border: 1px solid #e9ecef;
}
}
.light-component {
@include theme-style(light);
}
.dark-component {
@include theme-style(dark);
}
.primary-component {
@include theme-style(primary);
}
循环与混合宏
scss
// 创建响应式网格类
@mixin grid-columns($max-columns: 12) {
@for $i from 1 through $max-columns {
.col-#{$i} {
width: percentage($i / $max-columns);
}
}
}
@include grid-columns(12);
// 基于映射创建样式
$button-variants: (
primary: #007bff,
secondary: #6c757d,
success: #28a745,
danger: #dc3545,
warning: #ffc107,
info: #17a2b8
);
@mixin button-variant($color) {
background-color: $color;
border-color: $color;
color: if(lightness($color) > 50, #212529, white);
&:hover {
background-color: darken($color, 7.5%);
border-color: darken($color, 10%);
color: if(lightness($color) > 50, #212529, white);
}
&.disabled,
&:disabled {
background-color: $color;
border-color: $color;
}
}
@each $name, $color in $button-variants {
.btn-#{$name} {
@include button-variant($color);
}
}
继承 (Extend/Inheritance)
继承允许一个选择器继承另一个选择器的所有样式。
基本继承
scss
// 基础消息样式
.message {
border: 1px solid #ccc;
padding: 1rem;
border-radius: 4px;
margin-bottom: 1rem;
}
// 继承基础样式
.success {
@extend .message;
border-color: #28a745;
background-color: #d4edda;
color: #155724;
}
.error {
@extend .message;
border-color: #dc3545;
background-color: #f8d7da;
color: #721c24;
}
.warning {
@extend .message;
border-color: #ffc107;
background-color: #fff3cd;
color: #856404;
}
// 编译后的CSS会优化选择器
// .message, .success, .error, .warning { ... }
继承与伪类
scss
.btn {
padding: 0.5rem 1rem;
border: none;
border-radius: 4px;
cursor: pointer;
text-decoration: none;
display: inline-block;
&:hover {
text-decoration: none;
}
&:focus {
outline: 2px solid #007bff;
outline-offset: 2px;
}
}
.btn-primary {
@extend .btn;
background-color: #007bff;
color: white;
}
.btn-secondary {
@extend .btn;
background-color: #6c757d;
color: white;
}
占位符选择器 (Placeholder Selectors)
使用%创建占位符选择器,只在被继承时才生成CSS:
scss
// 占位符选择器
%button-base {
padding: 0.5rem 1rem;
border: none;
border-radius: 4px;
cursor: pointer;
text-decoration: none;
display: inline-block;
transition: all 0.3s ease;
&:hover {
transform: translateY(-2px);
}
}
%form-element {
width: 100%;
padding: 0.75rem;
border: 1px solid #ced4da;
border-radius: 4px;
font-size: 1rem;
&:focus {
outline: none;
border-color: #007bff;
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
}
}
// 继承占位符选择器
.btn {
@extend %button-base;
background-color: #007bff;
color: white;
}
.form-input {
@extend %form-element;
}
复杂继承场景
scss
%card-base {
background: white;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
overflow: hidden;
}
%card-section {
padding: 1rem;
&:first-child {
padding-top: 1rem;
}
&:last-child {
padding-bottom: 1rem;
}
}
// 使用继承
.article-card {
@extend %card-base;
.card-header {
@extend %card-section;
border-bottom: 1px solid #eee;
font-weight: bold;
}
.card-body {
@extend %card-section;
}
.card-footer {
@extend %card-section;
border-top: 1px solid #eee;
background-color: #f8f9fa;
}
}
混合宏 vs 继承
混合宏的优势
scss
// 混合宏:每个使用的地方都会生成完整样式
@mixin responsive-padding($mobile: 1rem, $desktop: 2rem) {
padding: $mobile;
@media (min-width: 768px) {
padding: $desktop;
}
}
// 每次使用都会生成完整的媒体查询
.element1 {
@include responsive-padding(1rem, 2rem);
}
.element2 {
@include responsive-padding(0.5rem, 1.5rem);
}
// 这种方式适合需要不同参数的情况
继承的优势
scss
// 继承:共享相同样式,生成优化的CSS
%responsive-base {
padding: 1rem;
@media (min-width: 768px) {
padding: 2rem;
}
}
.component1 {
@extend %responsive-base;
background-color: #f8f9fa;
}
.component2 {
@extend %responsive-base;
background-color: #e9ecef;
}
// 编译后会生成优化的选择器组合
高级混合宏模式
工厂模式混合宏
scss
@mixin create-component($name, $styles) {
.#{$name} {
@each $property, $value in $styles {
#{$property}: $value;
}
}
}
// 使用工厂模式创建组件
@include create-component(button, (
padding: 10px 20px,
background-color: #007bff,
color: white,
border: none,
border-radius: 4px
));
@include create-component(card, (
background: white,
padding: 1rem,
border-radius: 8px,
box-shadow: 0 2px 4px rgba(0,0,0,0.1)
));
配置驱动的混合宏
scss
// 创建可配置的组件
@mixin configurable-component($config) {
@if map-get($config, has-padding) {
padding: map-get($config, padding-size, 1rem);
}
@if map-get($config, has-border) {
border: map-get($config, border-width, 1px) solid map-get($config, border-color, #ccc);
}
@if map-get($config, has-shadow) {
box-shadow: map-get($config, shadow-value, 0 2px 4px rgba(0,0,0,0.1));
}
@if map-get($config, has-radius) {
border-radius: map-get($config, radius-size, 4px);
}
}
// 配置映射
$card-config: (
has-padding: true,
padding-size: 1.5rem,
has-border: true,
border-color: #dee2e6,
has-shadow: true,
has-radius: true,
radius-size: 8px
);
$button-config: (
has-padding: true,
padding-size: 0.5rem 1rem,
has-border: false,
has-shadow: false,
has-radius: true
);
.card {
@include configurable-component($card-config);
}
.btn {
@include configurable-component($button-config);
}
最佳实践
1. 何时使用混合宏
scss
// 适合使用混合宏的场景:需要参数变化
@mixin responsive-font($mobile: 1rem, $tablet: 1.1rem, $desktop: 1.2rem) {
font-size: $mobile;
@media (min-width: 768px) {
font-size: $tablet;
}
@media (min-width: 1024px) {
font-size: $desktop;
}
}
.title {
@include responsive-font(1.5rem, 1.7rem, 2rem);
}
.subtitle {
@include responsive-font(1.2rem, 1.3rem, 1.5rem);
}
2. 何时使用继承
scss
// 适合使用继承的场景:共享相同基础样式
%form-control {
display: block;
width: 100%;
padding: 0.75rem;
font-size: 1rem;
line-height: 1.5;
color: #495057;
background-color: #fff;
background-clip: padding-box;
border: 1px solid #ced4da;
border-radius: 0.25rem;
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
}
.form-input {
@extend %form-control;
}
.form-textarea {
@extend %form-control;
height: auto;
}
.form-select {
@extend %form-control;
}
3. 混合宏和继承的组合使用
scss
// 创建基础样式(使用占位符)
%button-base {
display: inline-block;
font-weight: 400;
text-align: center;
white-space: nowrap;
vertical-align: middle;
user-select: none;
border: 1px solid transparent;
padding: 0.5rem 1rem;
font-size: 1rem;
line-height: 1.5;
border-radius: 0.25rem;
transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
}
// 定义样式变化的混合宏
@mixin button-variant($background, $border, $color: white) {
@extend %button-base;
background-color: $background;
border-color: $border;
color: $color;
&:hover {
background-color: darken($background, 7.5%);
border-color: darken($border, 10%);
color: $color;
}
&:focus {
box-shadow: 0 0 0 0.2rem rgba($background, 0.5);
}
}
.btn-primary {
@include button-variant(#007bff, #007bff);
}
.btn-success {
@include button-variant(#28a745, #28a745);
}
.btn-danger {
@include button-variant(#dc3545, #dc3545);
}
通过合理使用混合宏和继承,您可以创建灵活、可重用和易于维护的Sass代码。