【8月10日】💎 Less/Sass写出优雅代码的4个高级技巧
🎯 学习目标:掌握Less/Sass高级技巧,让你的CSS预处理器代码更加优雅和高效
📊 难度等级 :初级-中级
🏷️ 技术标签 :
#Less
#Sass
#CSS预处理器
#样式工程化
#前端开发
⏱️ 阅读时间:约8分钟
📖 引言
在现代前端开发中,CSS预处理器已经成为不可或缺的工具。无论是Less还是Sass,它们都为我们提供了变量、嵌套、混入等强大功能。但是,你真的发挥了它们的全部潜力吗?
很多开发者在使用Less/Sass时,往往只停留在基础的变量定义和简单嵌套上,错过了许多能够显著提升代码质量和开发效率的高级特性。今天分享4个Less/Sass的高级技巧,这些技巧不仅能让你的代码更加优雅,还能大幅提升样式的可维护性和复用性!
💡 核心技巧详解
1. 🚀 高级混入(Mixins)- 创建灵活的样式组件
问题场景:需要创建可复用的响应式样式组件,传统混入功能有限
响应式断点混入:
less
// Less 版本
// 定义断点变量
@mobile: 768px;
@tablet: 1024px;
@desktop: 1200px;
/**
* 响应式断点混入
* @description 根据设备类型生成对应的媒体查询
* @param {string} device - 设备类型 (mobile, tablet, desktop)
* @param {string} type - 查询类型 (max, min)
*/
.responsive(@device, @type: min) when (@device = mobile) {
@media (min-width: @mobile) when (@type = min) {
.content();
}
@media (max-width: (@mobile - 1px)) when (@type = max) {
.content();
}
}
.responsive(@device, @type: min) when (@device = tablet) {
@media (min-width: @tablet) when (@type = min) {
.content();
}
@media (max-width: (@tablet - 1px)) when (@type = max) {
.content();
}
}
.responsive(@device, @type: min) when (@device = desktop) {
@media (min-width: @desktop) when (@type = min) {
.content();
}
@media (max-width: (@desktop - 1px)) when (@type = max) {
.content();
}
}
// 使用示例
.header {
padding: 20px;
.responsive(tablet) {
.content() {
padding: 30px;
font-size: 18px;
}
}
.responsive(desktop) {
.content() {
padding: 40px;
font-size: 20px;
}
}
}
scss
// Sass 版本
$breakpoints: (
mobile: 768px,
tablet: 1024px,
desktop: 1200px
);
/**
* 响应式断点混入
* @description 根据断点名称生成媒体查询
* @param {string} $breakpoint - 断点名称
* @param {string} $type - 查询类型 (min-width, max-width)
*/
@mixin responsive($breakpoint, $type: min-width) {
@if map-has-key($breakpoints, $breakpoint) {
$value: map-get($breakpoints, $breakpoint);
@if $type == min-width {
@media (min-width: $value) {
@content;
}
} @else if $type == max-width {
@media (max-width: $value - 1px) {
@content;
}
}
} @else {
@warn "Breakpoint '#{$breakpoint}' not found in $breakpoints map.";
}
}
// 使用示例
.header {
padding: 20px;
@include responsive(tablet) {
padding: 30px;
font-size: 18px;
}
@include responsive(desktop) {
padding: 40px;
font-size: 20px;
}
}
动态按钮样式混入
scss
/**
* 动态按钮样式混入
* @description 根据参数生成不同风格的按钮
* @param {color} $bg-color - 背景颜色
* @param {color} $text-color - 文字颜色
* @param {string} $size - 按钮尺寸 (small, medium, large)
* @param {string} $style - 按钮风格 (solid, outline, ghost)
*/
@mixin button-style($bg-color: #007bff, $text-color: #fff, $size: medium, $style: solid) {
// 尺寸映射
$sizes: (
small: (padding: 8px 16px, font-size: 12px, border-radius: 4px),
medium: (padding: 12px 24px, font-size: 14px, border-radius: 6px),
large: (padding: 16px 32px, font-size: 16px, border-radius: 8px)
);
// 应用尺寸样式
@if map-has-key($sizes, $size) {
$size-props: map-get($sizes, $size);
padding: map-get($size-props, padding);
font-size: map-get($size-props, font-size);
border-radius: map-get($size-props, border-radius);
}
// 应用风格样式
@if $style == solid {
background-color: $bg-color;
color: $text-color;
border: 2px solid $bg-color;
&:hover {
background-color: darken($bg-color, 10%);
border-color: darken($bg-color, 10%);
}
} @else if $style == outline {
background-color: transparent;
color: $bg-color;
border: 2px solid $bg-color;
&:hover {
background-color: $bg-color;
color: $text-color;
}
} @else if $style == ghost {
background-color: transparent;
color: $bg-color;
border: 2px solid transparent;
&:hover {
background-color: rgba($bg-color, 0.1);
border-color: rgba($bg-color, 0.3);
}
}
// 通用样式
display: inline-block;
text-align: center;
text-decoration: none;
cursor: pointer;
transition: all 0.3s ease;
border-style: solid;
&:focus {
outline: none;
box-shadow: 0 0 0 3px rgba($bg-color, 0.25);
}
&:disabled {
opacity: 0.6;
cursor: not-allowed;
&:hover {
background-color: $bg-color;
border-color: $bg-color;
}
}
}
// 使用示例
.btn-primary {
@include button-style(#007bff, #fff, medium, solid);
}
.btn-secondary {
@include button-style(#6c757d, #fff, medium, outline);
}
.btn-large {
@include button-style(#28a745, #fff, large, ghost);
}
核心优势:
- 高度可复用,一次定义多处使用
- 参数化配置,提高样式灵活性
- 条件逻辑处理,智能化生成样式
- 集中管理,易于维护
2. 🔄 循环与条件逻辑 - 批量生成样式
问题场景:需要生成大量相似的工具类(间距、颜色等),手动编写效率低下
批量生成间距类:
scss
// Sass 版本 - 生成间距工具类
$spacings: (0, 4, 8, 12, 16, 20, 24, 32, 40, 48, 56, 64);
$directions: (
'': '',
't': '-top',
'r': '-right',
'b': '-bottom',
'l': '-left',
'x': '-left, padding-right',
'y': '-top, padding-bottom'
);
/**
* 生成间距工具类
* @description 批量生成 margin 和 padding 类
*/
@each $size in $spacings {
@each $dir-key, $dir-value in $directions {
// 生成 margin 类
.m#{$dir-key}-#{$size} {
@if $dir-key == 'x' {
margin-left: #{$size}px;
margin-right: #{$size}px;
} @else if $dir-key == 'y' {
margin-top: #{$size}px;
margin-bottom: #{$size}px;
} @else {
margin#{$dir-value}: #{$size}px;
}
}
// 生成 padding 类
.p#{$dir-key}-#{$size} {
@if $dir-key == 'x' {
padding-left: #{$size}px;
padding-right: #{$size}px;
} @else if $dir-key == 'y' {
padding-top: #{$size}px;
padding-bottom: #{$size}px;
} @else {
padding#{$dir-value}: #{$size}px;
}
}
}
}
// 生成的CSS示例:
// .m-0 { margin: 0px; }
// .mt-4 { margin-top: 4px; }
// .px-8 { padding-left: 8px; padding-right: 8px; }
// .py-12 { padding-top: 12px; padding-bottom: 12px; }
less
// Less 版本 - 生成网格系统
@grid-columns: 12;
@grid-breakpoints: mobile, tablet, desktop;
/**
* 生成响应式网格系统
* @description 批量生成网格列类
*/
.generate-grid-columns(@breakpoint: '') {
.loop(@i: 1) when (@i <= @grid-columns) {
.col@{breakpoint}-@{i} {
flex: 0 0 percentage(@i / @grid-columns);
max-width: percentage(@i / @grid-columns);
}
.loop(@i + 1);
}
.loop();
}
// 生成基础网格
.generate-grid-columns();
// 生成响应式网格
.responsive(mobile) {
.content() {
.generate-grid-columns('-sm');
}
}
.responsive(tablet) {
.content() {
.generate-grid-columns('-md');
}
}
.responsive(desktop) {
.content() {
.generate-grid-columns('-lg');
}
}
动态主题色彩生成
scss
// 主题色彩配置
$theme-colors: (
primary: #007bff,
secondary: #6c757d,
success: #28a745,
danger: #dc3545,
warning: #ffc107,
info: #17a2b8
);
$color-levels: (50, 100, 200, 300, 400, 500, 600, 700, 800, 900);
/**
* 生成主题色彩变量和工具类
* @description 为每个主题色生成不同深浅的变体
*/
@each $color-name, $color-value in $theme-colors {
// 生成基础颜色变量
--color-#{$color-name}: #{$color-value};
// 生成不同深浅的颜色变体
@each $level in $color-levels {
$lightness-adjustment: (500 - $level) * 0.1;
@if $level < 500 {
--color-#{$color-name}-#{$level}: #{lighten($color-value, abs($lightness-adjustment))};
} @else if $level > 500 {
--color-#{$color-name}-#{$level}: #{darken($color-value, $lightness-adjustment)};
} @else {
--color-#{$color-name}-#{$level}: #{$color-value};
}
}
// 生成工具类
.text-#{$color-name} {
color: var(--color-#{$color-name});
}
.bg-#{$color-name} {
background-color: var(--color-#{$color-name});
}
.border-#{$color-name} {
border-color: var(--color-#{$color-name});
}
// 生成不同深浅的工具类
@each $level in $color-levels {
.text-#{$color-name}-#{$level} {
color: var(--color-#{$color-name}-#{$level});
}
.bg-#{$color-name}-#{$level} {
background-color: var(--color-#{$color-name}-#{$level});
}
}
}
实际应用场景
html
<!-- 使用生成的工具类 -->
<div class="container">
<div class="row">
<div class="col-md-6 p-16">
<h2 class="text-primary-600 mb-8">标题</h2>
<p class="text-secondary-500 mt-4">内容文本</p>
</div>
<div class="col-md-6 bg-success-100 p-24">
<button class="bg-primary text-white px-16 py-8">按钮</button>
</div>
</div>
</div>
核心优势:
- 批量生成大量工具类,提高开发效率
- 保持命名和数值的统一性
- 易于扩展,只需修改配置即可
- 代码更加简洁,减少文件大小
3. 🎨 模块化主题系统 - 构建可扩展的设计系统
问题场景:需要支持多主题切换(深色模式、品牌主题),传统方式维护成本高
主题系统架构:
scss
// _theme-config.scss - 主题配置文件
$themes: (
light: (
// 基础色彩
primary: #007bff,
secondary: #6c757d,
success: #28a745,
danger: #dc3545,
warning: #ffc107,
info: #17a2b8,
// 语义化色彩
background: #ffffff,
surface: #f8f9fa,
text-primary: #212529,
text-secondary: #6c757d,
text-muted: #adb5bd,
border: #dee2e6,
shadow: rgba(0, 0, 0, 0.1),
// 状态色彩
hover-overlay: rgba(0, 0, 0, 0.05),
active-overlay: rgba(0, 0, 0, 0.1),
disabled-overlay: rgba(0, 0, 0, 0.3)
),
dark: (
// 基础色彩
primary: #0d6efd,
secondary: #6c757d,
success: #198754,
danger: #dc3545,
warning: #fd7e14,
info: #0dcaf0,
// 语义化色彩
background: #121212,
surface: #1e1e1e,
text-primary: #ffffff,
text-secondary: #adb5bd,
text-muted: #6c757d,
border: #495057,
shadow: rgba(0, 0, 0, 0.3),
// 状态色彩
hover-overlay: rgba(255, 255, 255, 0.05),
active-overlay: rgba(255, 255, 255, 0.1),
disabled-overlay: rgba(255, 255, 255, 0.3)
),
brand: (
// 自定义品牌主题
primary: #6f42c1,
secondary: #e83e8c,
success: #20c997,
danger: #fd7e14,
warning: #ffc107,
info: #6f42c1,
background: #f8f5ff,
surface: #ffffff,
text-primary: #2d1b69,
text-secondary: #6f42c1,
text-muted: #8e6bb8,
border: #d1c4e9,
shadow: rgba(111, 66, 193, 0.15),
hover-overlay: rgba(111, 66, 193, 0.05),
active-overlay: rgba(111, 66, 193, 0.1),
disabled-overlay: rgba(111, 66, 193, 0.3)
)
);
/**
* 获取主题变量
* @description 从主题配置中获取指定变量值
* @param {string} $theme-name - 主题名称
* @param {string} $key - 变量键名
* @returns {color} 主题变量值
*/
@function theme-get($theme-name, $key) {
$theme: map-get($themes, $theme-name);
@if $theme {
@return map-get($theme, $key);
}
@warn "Theme '#{$theme-name}' not found";
@return null;
}
/**
* 应用主题样式
* @description 为指定主题生成样式
* @param {string} $theme-name - 主题名称
*/
@mixin apply-theme($theme-name) {
$theme: map-get($themes, $theme-name);
@if $theme {
@each $key, $value in $theme {
--#{$key}: #{$value};
}
}
}
/**
* 主题感知混入
* @description 根据当前主题应用不同样式
* @param {map} $theme-styles - 主题样式映射
*/
@mixin theme-aware($theme-styles) {
@each $theme-name, $styles in $theme-styles {
[data-theme="#{$theme-name}"] & {
@each $property, $value in $styles {
#{$property}: #{$value};
}
}
}
}
主题应用示例
scss
// _components.scss - 组件样式
/**
* 卡片组件
* @description 支持多主题的卡片组件
*/
.card {
background-color: var(--surface);
border: 1px solid var(--border);
border-radius: 8px;
padding: 24px;
box-shadow: 0 2px 8px var(--shadow);
transition: all 0.3s ease;
&:hover {
box-shadow: 0 4px 16px var(--shadow);
transform: translateY(-2px);
}
.card-title {
color: var(--text-primary);
font-size: 18px;
font-weight: 600;
margin-bottom: 12px;
}
.card-content {
color: var(--text-secondary);
line-height: 1.6;
}
.card-footer {
margin-top: 16px;
padding-top: 16px;
border-top: 1px solid var(--border);
}
}
/**
* 按钮组件 - 主题感知版本
*/
.btn {
padding: 12px 24px;
border-radius: 6px;
font-weight: 500;
text-decoration: none;
display: inline-block;
transition: all 0.3s ease;
cursor: pointer;
border: 2px solid transparent;
// 主题感知样式
@include theme-aware((
light: (
background-color: var(--primary),
color: white,
border-color: var(--primary)
),
dark: (
background-color: var(--primary),
color: var(--background),
border-color: var(--primary)
),
brand: (
background: linear-gradient(135deg, var(--primary), var(--secondary)),
color: white,
border-color: transparent
)
));
&:hover {
transform: translateY(-1px);
@include theme-aware((
light: (
background-color: darken(theme-get(light, primary), 10%),
box-shadow: 0 4px 12px rgba(theme-get(light, primary), 0.3)
),
dark: (
background-color: lighten(theme-get(dark, primary), 10%),
box-shadow: 0 4px 12px rgba(theme-get(dark, primary), 0.4)
),
brand: (
box-shadow: 0 6px 20px rgba(theme-get(brand, primary), 0.4),
filter: brightness(1.1)
)
));
}
&.btn-outline {
background-color: transparent;
color: var(--primary);
border-color: var(--primary);
&:hover {
background-color: var(--primary);
color: var(--background);
}
}
}
/**
* 导航栏组件
*/
.navbar {
background-color: var(--surface);
border-bottom: 1px solid var(--border);
padding: 16px 0;
.navbar-brand {
color: var(--text-primary);
font-size: 20px;
font-weight: 700;
text-decoration: none;
}
.navbar-nav {
display: flex;
list-style: none;
margin: 0;
padding: 0;
.nav-item {
margin: 0 8px;
.nav-link {
color: var(--text-secondary);
text-decoration: none;
padding: 8px 16px;
border-radius: 4px;
transition: all 0.3s ease;
&:hover {
color: var(--text-primary);
background-color: var(--hover-overlay);
}
&.active {
color: var(--primary);
background-color: var(--hover-overlay);
}
}
}
}
.theme-toggle {
background: none;
border: 1px solid var(--border);
color: var(--text-secondary);
padding: 8px 12px;
border-radius: 4px;
cursor: pointer;
transition: all 0.3s ease;
&:hover {
color: var(--text-primary);
border-color: var(--primary);
}
}
}
主题切换实现
scss
// _theme-generator.scss - 主题生成器
/**
* 生成所有主题的CSS变量
*/
:root {
@include apply-theme(light);
}
[data-theme="light"] {
@include apply-theme(light);
}
[data-theme="dark"] {
@include apply-theme(dark);
}
[data-theme="brand"] {
@include apply-theme(brand);
}
/**
* 主题过渡动画
*/
* {
transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease;
}
typescript
// theme-switcher.ts - 主题切换逻辑
/**
* 主题管理器
* @description 管理主题切换和持久化
*/
class ThemeManager {
private currentTheme: string = 'light';
private readonly STORAGE_KEY = 'app-theme';
constructor() {
this.initTheme();
}
/**
* 初始化主题
* @description 从本地存储读取主题设置
*/
private initTheme = (): void => {
const savedTheme = localStorage.getItem(this.STORAGE_KEY);
const systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
this.currentTheme = savedTheme || (systemPrefersDark ? 'dark' : 'light');
this.applyTheme(this.currentTheme);
// 监听系统主题变化
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
if (!savedTheme) {
this.setTheme(e.matches ? 'dark' : 'light');
}
});
};
/**
* 设置主题
* @description 切换到指定主题
* @param {string} theme - 主题名称
*/
setTheme = (theme: string): void => {
this.currentTheme = theme;
this.applyTheme(theme);
localStorage.setItem(this.STORAGE_KEY, theme);
// 触发主题变化事件
window.dispatchEvent(new CustomEvent('themechange', {
detail: { theme }
}));
};
/**
* 应用主题
* @description 将主题应用到DOM
* @param {string} theme - 主题名称
*/
private applyTheme = (theme: string): void => {
document.documentElement.setAttribute('data-theme', theme);
};
/**
* 获取当前主题
* @returns {string} 当前主题名称
*/
getCurrentTheme = (): string => {
return this.currentTheme;
};
/**
* 切换主题
* @description 在预设主题间循环切换
*/
toggleTheme = (): void => {
const themes = ['light', 'dark', 'brand'];
const currentIndex = themes.indexOf(this.currentTheme);
const nextIndex = (currentIndex + 1) % themes.length;
this.setTheme(themes[nextIndex]);
};
}
// 导出单例实例
export const themeManager = new ThemeManager();
使用示例
html
<!-- HTML 结构 -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>主题系统演示</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<nav class="navbar">
<div class="container">
<a href="#" class="navbar-brand">我的应用</a>
<ul class="navbar-nav">
<li class="nav-item">
<a href="#" class="nav-link active">首页</a>
</li>
<li class="nav-item">
<a href="#" class="nav-link">关于</a>
</li>
<li class="nav-item">
<a href="#" class="nav-link">联系</a>
</li>
</ul>
<button class="theme-toggle" onclick="themeManager.toggleTheme()">
🌓 切换主题
</button>
</div>
</nav>
<main class="container mt-32">
<div class="card">
<h2 class="card-title">欢迎使用主题系统</h2>
<div class="card-content">
<p>这是一个支持多主题切换的演示页面。点击右上角的按钮可以在浅色、深色和品牌主题之间切换。</p>
</div>
<div class="card-footer">
<a href="#" class="btn">主要按钮</a>
<a href="#" class="btn btn-outline ml-8">次要按钮</a>
</div>
</div>
</main>
<script src="theme-switcher.js"></script>
</body>
</html>
核心优势:
- 统一管理所有主题配置,易于维护
- 类型安全,确保主题变量正确使用
- 支持运行时动态切换,无需重新加载
- 扩展性强,添加新主题简单
- 性能优化,使用CSS变量避免重复计算
4. ⚡ 性能优化与最佳实践 - 提升编译效率
问题场景:不当使用预处理器导致编译时间过长、输出文件过大
智能导入和模块化:
scss
// _index.scss - 主入口文件
// 使用 @use 替代 @import,避免重复导入
@use 'sass:math';
@use 'sass:color';
@use 'sass:string';
// 基础配置(只导入一次)
@use 'config/variables' as vars;
@use 'config/functions' as fn;
@use 'config/mixins' as mx;
// 基础样式
@use 'base/reset';
@use 'base/typography';
@use 'base/layout';
// 组件样式(按需导入)
@use 'components/button';
@use 'components/card';
@use 'components/form';
@use 'components/navigation';
// 工具类(条件导入)
@if vars.$include-utilities {
@use 'utilities/spacing';
@use 'utilities/colors';
@use 'utilities/typography';
}
// 主题(条件导入)
@if vars.$enable-themes {
@use 'themes/light';
@use 'themes/dark';
}
scss
// _variables.scss - 配置变量
// 功能开关,控制编译内容
$include-utilities: true !default;
$enable-themes: true !default;
$enable-animations: true !default;
$enable-grid: true !default;
$enable-responsive: true !default;
// 性能相关配置
$compile-mode: 'production' !default; // development | production
$output-style: 'compressed' !default; // expanded | compressed
$source-maps: false !default;
// 断点配置(只定义需要的断点)
$breakpoints: (
sm: 576px,
md: 768px,
lg: 992px,
xl: 1200px
) !default;
// 颜色配置(使用更高效的颜色函数)
$primary-color: #007bff !default;
$color-palette: (
50: color.mix(white, $primary-color, 95%),
100: color.mix(white, $primary-color, 90%),
200: color.mix(white, $primary-color, 75%),
300: color.mix(white, $primary-color, 50%),
400: color.mix(white, $primary-color, 25%),
500: $primary-color,
600: color.mix(black, $primary-color, 25%),
700: color.mix(black, $primary-color, 50%),
800: color.mix(black, $primary-color, 75%),
900: color.mix(black, $primary-color, 90%)
) !default;
高效的混入和函数
scss
// _mixins.scss - 优化的混入
/**
* 高效的响应式混入
* @description 使用内容块和条件编译优化性能
* @param {string} $breakpoint - 断点名称
* @param {string} $direction - 方向 (up, down, between)
*/
@mixin respond-to($breakpoint, $direction: up) {
@if map-has-key(vars.$breakpoints, $breakpoint) {
$value: map-get(vars.$breakpoints, $breakpoint);
@if $direction == up {
@media (min-width: $value) {
@content;
}
} @else if $direction == down {
@media (max-width: $value - 1px) {
@content;
}
}
} @else {
@error "Breakpoint '#{$breakpoint}' not found in $breakpoints map.";
}
}
/**
* 缓存优化的阴影混入
* @description 预定义常用阴影,避免重复计算
*/
$shadow-cache: (
sm: 0 1px 3px rgba(0, 0, 0, 0.12),
md: 0 4px 6px rgba(0, 0, 0, 0.1),
lg: 0 10px 15px rgba(0, 0, 0, 0.1),
xl: 0 20px 25px rgba(0, 0, 0, 0.1)
);
@mixin box-shadow($size: md) {
@if map-has-key($shadow-cache, $size) {
box-shadow: map-get($shadow-cache, $size);
} @else {
@warn "Shadow size '#{$size}' not found. Using default 'md'.";
box-shadow: map-get($shadow-cache, md);
}
}
/**
* 智能字体加载
* @description 根据环境选择最优字体加载策略
*/
@mixin font-face($name, $path, $weight: normal, $style: normal) {
@font-face {
font-family: $name;
font-weight: $weight;
font-style: $style;
font-display: swap; // 优化字体加载性能
@if vars.$compile-mode == 'production' {
// 生产环境:使用压缩格式
src: url('#{$path}.woff2') format('woff2'),
url('#{$path}.woff') format('woff');
} @else {
// 开发环境:包含更多格式用于调试
src: url('#{$path}.woff2') format('woff2'),
url('#{$path}.woff') format('woff'),
url('#{$path}.ttf') format('truetype');
}
}
}
条件编译和代码分割
scss
// _utilities.scss - 条件生成工具类
/**
* 智能工具类生成
* @description 根据配置条件生成工具类,避免不必要的代码
*/
@if vars.$include-utilities {
// 间距工具类(只在需要时生成)
@if vars.$enable-spacing-utilities {
$spacing-values: (0, 4, 8, 12, 16, 20, 24, 32, 40, 48);
$spacing-properties: (
m: margin,
p: padding
);
$spacing-directions: (
t: top,
r: right,
b: bottom,
l: left,
x: (left, right),
y: (top, bottom)
);
@each $prop-key, $prop-value in $spacing-properties {
@each $value in $spacing-values {
// 全方向
.#{$prop-key}-#{$value} {
#{$prop-value}: #{$value}px;
}
// 特定方向
@each $dir-key, $dir-value in $spacing-directions {
.#{$prop-key}#{$dir-key}-#{$value} {
@if type-of($dir-value) == list {
@each $direction in $dir-value {
#{$prop-value}-#{$direction}: #{$value}px;
}
} @else {
#{$prop-value}-#{$dir-value}: #{$value}px;
}
}
}
}
}
}
// 响应式工具类(按需生成)
@if vars.$enable-responsive {
@each $breakpoint-name, $breakpoint-value in vars.$breakpoints {
@include respond-to($breakpoint-name) {
// 只为大屏幕生成显示/隐藏类
.d-#{$breakpoint-name}-block { display: block; }
.d-#{$breakpoint-name}-none { display: none; }
.d-#{$breakpoint-name}-flex { display: flex; }
.d-#{$breakpoint-name}-grid { display: grid; }
}
}
}
}
编译优化配置
javascript
// webpack.config.js - Webpack 配置优化
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
module.exports = {
module: {
rules: [
{
test: /\.(scss|sass)$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
sourceMap: process.env.NODE_ENV === 'development'
}
},
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [
['autoprefixer'],
['cssnano', { preset: 'default' }]
]
}
}
},
{
loader: 'sass-loader',
options: {
implementation: require('sass'), // 使用 Dart Sass
sassOptions: {
outputStyle: 'compressed',
includePaths: [path.resolve(__dirname, 'src/styles')]
},
additionalData: `
@use 'config/variables' as vars;
@use 'config/mixins' as mx;
`
}
}
]
}
]
},
plugins: [
new MiniCssExtractPlugin({
filename: '[name].[contenthash].css',
chunkFilename: '[id].[contenthash].css'
})
],
optimization: {
minimizer: [
new CssMinimizerPlugin({
minimizerOptions: {
preset: [
'default',
{
discardComments: { removeAll: true },
normalizeWhitespace: true
}
]
}
})
],
splitChunks: {
cacheGroups: {
styles: {
name: 'styles',
type: 'css/mini-extract',
chunks: 'all',
enforce: true
}
}
}
}
};
性能监控和分析
javascript
// build-analyzer.js - 构建分析工具
const fs = require('fs');
const path = require('path');
const sass = require('sass');
/**
* 分析 Sass 编译性能
* @description 监控编译时间和输出大小
*/
class SassAnalyzer {
constructor() {
this.startTime = 0;
this.endTime = 0;
this.stats = {
compileTime: 0,
inputSize: 0,
outputSize: 0,
compressionRatio: 0
};
}
/**
* 开始分析
* @param {string} inputFile - 输入文件路径
*/
analyze = async (inputFile) => {
this.startTime = Date.now();
try {
// 获取输入文件大小
const inputStats = fs.statSync(inputFile);
this.stats.inputSize = inputStats.size;
// 编译 Sass
const result = sass.compile(inputFile, {
style: 'compressed',
sourceMap: false
});
this.endTime = Date.now();
this.stats.compileTime = this.endTime - this.startTime;
this.stats.outputSize = Buffer.byteLength(result.css, 'utf8');
this.stats.compressionRatio = (
(this.stats.inputSize - this.stats.outputSize) / this.stats.inputSize * 100
).toFixed(2);
this.logResults();
return result;
} catch (error) {
console.error('Sass compilation failed:', error);
throw error;
}
};
/**
* 输出分析结果
*/
logResults = () => {
console.log('\n📊 Sass Compilation Analysis:');
console.log(`⏱️ Compile Time: ${this.stats.compileTime}ms`);
console.log(`📥 Input Size: ${(this.stats.inputSize / 1024).toFixed(2)}KB`);
console.log(`📤 Output Size: ${(this.stats.outputSize / 1024).toFixed(2)}KB`);
console.log(`🗜️ Compression: ${this.stats.compressionRatio}%`);
// 性能建议
if (this.stats.compileTime > 5000) {
console.warn('⚠️ Compilation time is high. Consider optimizing your Sass code.');
}
if (this.stats.outputSize > 100 * 1024) {
console.warn('⚠️ Output size is large. Consider code splitting or removing unused styles.');
}
};
}
// 使用示例
const analyzer = new SassAnalyzer();
analyzer.analyze('./src/styles/main.scss');
性能优化清单
编译时优化
- ✅ 使用
@use
替代@import
,避免重复导入 - ✅ 实现条件编译,只生成需要的代码
- ✅ 合理使用变量和混入,避免过度嵌套
- ✅ 启用 Sass 缓存,加速重复编译
- ✅ 使用 Dart Sass 替代 Node Sass,获得更好性能
运行时优化
- ✅ 启用 CSS 压缩和优化
- ✅ 使用 CSS 变量减少重复计算
- ✅ 实现代码分割,按需加载样式
- ✅ 启用 Gzip/Brotli 压缩
- ✅ 使用 CDN 加速样式文件加载
维护性优化
- ✅ 建立清晰的文件组织结构
- ✅ 使用命名约定,提高代码可读性
- ✅ 实现自动化测试,确保样式质量
- ✅ 建立性能监控,及时发现问题
- ✅ 定期重构,清理无用代码
核心优势:
- 编译效率显著提升,减少编译时间
- 输出优化,生成更小更高效的CSS文件
- 保持快速的开发反馈循环
- 清晰的代码结构,便于长期维护
- 模块化设计,易于添加新功能
📊 技巧对比与选择指南
技巧 | 适用场景 | 学习难度 | 性能影响 | 维护成本 |
---|---|---|---|---|
高级混入 | 组件库开发、样式复用 | 中等 | 正面 | 低 |
循环与条件 | 工具类生成、批量样式 | 中等 | 中性 | 低 |
主题系统 | 多主题应用、设计系统 | 高 | 正面 | 中等 |
性能优化 | 大型项目、生产环境 | 高 | 显著正面 | 中等 |
选择建议
- 小型项目:重点使用高级混入和基础循环
- 中型项目:添加主题系统,提升用户体验
- 大型项目:全面应用所有技巧,重点关注性能优化
- 组件库:优先实现高级混入和主题系统
- 企业应用:建立完整的设计系统和性能监控
🔗 相关资源
💡 今日收获:掌握了4个Less/Sass高级技巧,包括灵活的混入系统、智能的循环生成、模块化主题架构和全面的性能优化策略。这些技巧能够显著提升样式代码的质量、可维护性和开发效率。
如果这篇文章对你有帮助,欢迎点赞、收藏和分享!有任何问题也欢迎在评论区讨论。 🚀