Appearance
Sass 性能优化
Sass提供了许多功能来帮助您编写更高效、更优化的样式代码。本章将详细介绍如何优化Sass代码以提高编译速度和生成更高效的CSS。
编译性能优化
优化编译配置
scss
// sass.config.js 或 webpack配置中的Sass选项
module.exports = {
implementation: require('sass'),
sassOptions: {
// 输出样式:压缩模式生成最小的CSS
outputStyle: 'compressed', // 'nested', 'expanded', 'compact', 'compressed'
// 源映射设置
sourceMap: true, // 开发时启用,生产时可禁用
sourceMapEmbed: false, // 不将源码嵌入CSS
sourceMapContents: false, // 不包含源码内容
// 性能优化选项
omitSourceMapUrl: false, // 保留源映射URL
sourceMapRoot: '../', // 源映射根路径
// 包含路径
includePaths: [
'./node_modules',
'./src/scss',
'./src/styles'
],
// 精简输出
indentWidth: 2, // 缩进宽度
quietDeps: true, // 隐藏依赖文件的警告
verbose: false // 不输出详细信息
}
};
项目结构优化
scss
// 推荐的Sass项目结构
// scss/
// ├── abstracts/
// │ ├── _variables.scss // Sass变量
// │ ├── _functions.scss // Sass函数
// │ ├── _mixins.scss // Sass混合宏
// │ └── _placeholders.scss // Sass占位符
// ├── base/
// │ ├── _reset.scss // 重置样式
// │ ├── _typography.scss // 排版样式
// │ └── _helpers.scss // 辅助类
// ├── components/
// │ ├── _buttons.scss // 按钮组件
// │ ├── _cards.scss // 卡片组件
// │ └── _forms.scss // 表单组件
// ├── layout/
// │ ├── _header.scss // 页眉布局
// │ ├── _footer.scss // 页脚布局
// │ └── _navigation.scss // 导航布局
// ├── pages/
// │ ├── _home.scss // 首页样式
// │ └── _contact.scss // 联系页面样式
// ├── themes/
// │ └── _default.scss // 主题样式
// └── main.scss // 主入口文件
// main.scss
// 抽象层
@use 'abstracts/variables';
@use 'abstracts/functions';
@use 'abstracts/mixins';
// 基础样式
@use 'base/reset';
@use 'base/typography';
@use 'base/helpers';
// 布局样式
@use 'layout/header';
@use 'layout/footer';
@use 'layout/navigation';
// 组件样式
@use 'components/buttons';
@use 'components/cards';
@use 'components/forms';
// 页面样式
@use 'pages/home';
@use 'pages/contact';
// 主题样式
@use 'themes/default';
代码结构优化
避免嵌套过深
scss
// 不推荐:嵌套过深,生成冗长的选择器
.header {
.nav {
.menu {
.item {
.link { // 生成 .header .nav .menu .item .link
color: #333;
&:hover {
color: #007bff;
}
}
}
}
}
}
// 推荐:使用语义化类名
.header {
// header样式
}
.nav-menu {
// menu样式
}
.nav-item {
// item样式
}
.nav-link {
color: #333;
&:hover {
color: #007bff;
}
}
// 使用BEM命名方法
.card {
background: white;
border-radius: 8px;
&__header {
padding: 1rem;
border-bottom: 1px solid #eee;
}
&__title {
margin: 0;
font-size: 1.25rem;
}
&__body {
padding: 1rem;
}
&__footer {
padding: 1rem;
border-top: 1px solid #eee;
}
&--featured {
border: 2px solid #007bff;
}
}
合理使用继承
scss
// 占位符选择器优化CSS输出
%button-base {
display: inline-block;
padding: 0.5rem 1rem;
border: none;
border-radius: 4px;
cursor: pointer;
text-align: center;
text-decoration: none;
transition: all 0.3s ease;
}
%form-element {
width: 100%;
padding: 0.75rem;
border: 1px solid #ced4da;
border-radius: 4px;
font-size: 1rem;
box-sizing: border-box;
}
// 使用占位符
.btn {
@extend %button-base;
background-color: #007bff;
color: white;
&:hover {
background-color: darken(#007bff, 10%);
}
}
.form-input {
@extend %form-element;
&:focus {
outline: none;
border-color: #007bff;
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
}
}
// 避免不必要的继承
// 不推荐
%text-base {
font-family: Arial, sans-serif;
font-size: 16px;
line-height: 1.5;
}
.text-title {
@extend %text-base;
font-size: 2rem;
font-weight: bold;
}
.text-paragraph {
@extend %text-base;
font-size: 1rem;
}
// 推荐:使用混合宏
@mixin text-base {
font-family: Arial, sans-serif;
line-height: 1.5;
}
.text-title {
@include text-base;
font-size: 2rem;
font-weight: bold;
}
.text-paragraph {
@include text-base;
font-size: 1rem;
}
变量和函数优化
高效的变量使用
scss
// 集中管理变量
// _variables.scss
// 颜色变量
$colors: (
'primary': #007bff,
'secondary': #6c757d,
'success': #28a745,
'danger': #dc3545,
'warning': #ffc107,
'info': #17a2b8,
'light': #f8f9fa,
'dark': #343a40
);
// 间距变量
$spacers: (
0: 0,
1: 0.25rem,
2: 0.5rem,
3: 1rem,
4: 1.5rem,
5: 3rem
);
// 断点变量
$breakpoints: (
'xs': 0,
'sm': 576px,
'md': 768px,
'lg': 992px,
'xl': 1200px,
'xxl': 1400px
);
// 字体变量
$font-family-sans-serif: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
$font-family-serif: Georgia, 'Times New Roman', Times, serif;
$font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;
$font-size-base: 1rem;
$font-weight-normal: 400;
$font-weight-bold: 700;
// 生成工具类
@each $name, $value in $colors {
.text-#{$name} {
color: $value;
}
.bg-#{$name} {
background-color: $value;
}
.border-#{$name} {
border-color: $value;
}
}
@each $spacer, $size in $spacers {
.m-#{$spacer} { margin: $size; }
.p-#{$spacer} { padding: $size; }
}
高效函数编写
scss
// 优化的函数实现
@function theme-color($key) {
@return map-get($colors, $key);
}
@function spacing($level) {
@return map-get($spacers, $level);
}
@function breakpoint($key) {
@return map-get($breakpoints, $key);
}
// 避免复杂计算函数
@function pow($number, $exponent) {
@if $exponent == 0 { @return 1; }
@if $exponent == 1 { @return $number; }
$result: $number;
$counter: 1;
@while $counter < abs($exponent) {
$result: $result * $number;
$counter: $counter + 1;
}
@if $exponent < 0 {
$result: 1 / $result;
}
@return $result;
}
// 黄金比例函数
@function golden-ratio($value, $increment: 1) {
$phi: 1.618033988749895;
@return $value * pow($phi, $increment);
}
// 颜色对比度函数
@function contrast-ratio($background, $foreground: white) {
$back-lum: luminance($background);
$fore-lum: luminance($foreground);
@return if($back-lum > $fore-lum,
($back-lum + 0.05) / ($fore-lum + 0.05),
($fore-lum + 0.05) / ($back-lum + 0.05)
);
}
@function luminance($color) {
$r: red($color) / 255;
$g: green($color) / 255;
$b: blue($color) / 255;
$r: if($r < 0.03928, $r / 12.92, pow(($r + 0.055) / 1.055, 2.4));
$g: if($g < 0.03928, $g / 12.92, pow(($g + 0.055) / 1.055, 2.4));
$b: if($b < 0.03928, $b / 12.92, pow(($b + 0.055) / 1.055, 2.4));
@return 0.2126 * $r + 0.7152 * $g + 0.0722 * $b;
}
// 使用高效函数
.card {
background-color: theme-color('light');
padding: spacing(3);
.card-title {
color: if(contrast-ratio(theme-color('light'), theme-color('dark')) > 4.5,
theme-color('dark'),
theme-color('primary')
);
}
}
混合宏优化
高效混合宏编写
scss
// 响应式混合宏优化
@mixin respond-to($breakpoint) {
@if map-has-key($breakpoints, $breakpoint) {
@media (min-width: map-get($breakpoints, $breakpoint)) {
@content;
}
}
}
// 按钮变体混合宏
@mixin button-variant($bg, $border: $bg, $color: color-contrast($bg)) {
background-color: $bg;
border-color: $border;
color: $color;
&:hover,
&:focus,
&.focus {
background-color: darken($bg, 7.5%);
border-color: darken($border, 10%);
color: $color;
}
&.disabled,
&:disabled {
background-color: $bg;
border-color: $border;
}
}
// 颜色对比函数
@function color-contrast($color) {
@return if(lightness($color) > 50, #212529, #fff);
}
// 使用混合宏
.btn-primary {
@include button-variant(theme-color('primary'));
}
.btn-success {
@include button-variant(theme-color('success'));
}
.btn-warning {
@include button-variant(theme-color('warning'), $color: #212529);
}
// 避免过度复杂的混合宏
// 不推荐
@mixin complex-component($options) {
@if map-get($options, 'has-padding') {
padding: map-get($options, 'padding-size', 1rem);
}
@if map-get($options, 'has-margin') {
margin: map-get($options, 'margin-size', 0);
}
@if map-get($options, 'has-border') {
border: map-get($options, 'border-width', 1px) solid map-get($options, 'border-color', #ccc);
}
// 更多复杂逻辑...
}
// 推荐:简单、专注的混合宏
@mixin padding($size: 1rem) {
padding: $size;
}
@mixin margin($size: 0) {
margin: $size;
}
@mixin border($width: 1px, $style: solid, $color: #ccc) {
border: $width $style $color;
}
循环优化
scss
// 优化循环以避免生成过多CSS
// 生成有限的网格类
$grid-columns: 12;
@for $i from 1 through $grid-columns {
.col-#{$i} {
width: percentage($i / $grid-columns);
}
}
// 生成响应式网格类
@each $breakpoint, $value in $breakpoints {
@if $breakpoint != 'xs' {
@media (min-width: $value) {
@for $i from 1 through $grid-columns {
.col-#{$breakpoint}-#{$i} {
width: percentage($i / $grid-columns);
}
}
}
}
}
// 生成字体大小类(限制范围以避免过多类)
@for $i from 1 through 10 {
$size: $i * 0.25rem;
.fs-#{$i} {
font-size: $size;
}
}
// 生成响应式字体大小
$font-sizes: (1: 0.75rem, 2: 1rem, 3: 1.25rem, 4: 1.5rem, 5: 2rem, 6: 2.5rem);
@each $level, $size in $font-sizes {
.fs-#{$level} {
font-size: $size;
}
@include respond-to(md) {
.fs-md-#{$level} {
font-size: $size;
}
}
@include respond-to(lg) {
.fs-lg-#{$level} {
font-size: $size * 1.2; // 稍微放大
}
}
}
CSS输出优化
优化选择器
scss
// 避免生成过长的选择器链
// 不推荐
.component {
.sub-component {
.element {
.item {
.detail { // 生成非常长的选择器
color: red;
}
}
}
}
}
// 推荐:使用语义化类名
.component-detail {
color: red;
}
// 使用命名空间减少冲突
$namespace: 'myapp';
.#{$namespace}-button {
padding: 10px 20px;
&--primary {
background-color: #007bff;
color: white;
}
&--secondary {
background-color: #6c757d;
color: white;
}
}
// 避免重复属性
// 不推荐
.duplicate-properties {
background-color: #f8f9fa;
background-image: url('bg.png');
background-repeat: no-repeat;
background-position: center;
}
// 推荐:使用简写属性
.optimized-properties {
background: #f8f9fa url('bg.png') no-repeat center;
}
优化媒体查询
scss
// 避免重复的媒体查询
// 不推荐
.header {
padding: 1rem;
@media (min-width: 768px) {
padding: 1.5rem;
}
}
.nav {
display: block;
@media (min-width: 768px) { // 重复的媒体查询
display: flex;
}
}
// 推荐:集中管理响应式样式
$grid-breakpoints: (
xs: 0,
sm: 576px,
md: 768px,
lg: 992px,
xl: 1200px,
xxl: 1400px
);
// 创建响应式混合宏
@mixin media-breakpoint-up($name, $breakpoints: $grid-breakpoints) {
$min: map-get($breakpoints, $name);
@if $min != 0 {
@media (min-width: $min) {
@content;
}
} @else {
@content;
}
}
// 使用混合宏
.header {
padding: 1rem;
@include media-breakpoint-up(md) {
padding: 1.5rem;
}
}
.nav {
display: block;
@include media-breakpoint-up(md) {
display: flex;
}
}
// 响应式网格系统
@mixin make-grid-columns($columns: $grid-columns, $gutter: 30px, $breakpoints: $grid-breakpoints) {
%grid-column {
position: relative;
width: 100%;
padding-right: $gutter / 2;
padding-left: $gutter / 2;
}
@for $i from 1 through $columns {
.col-#{$i} {
@extend %grid-column;
flex: 0 0 percentage($i / $columns);
max-width: percentage($i / $columns);
}
}
@each $breakpoint, $value in $breakpoints {
@if $breakpoint != 'xs' {
@include media-breakpoint-up($breakpoint, $breakpoints) {
%grid-column-#{$breakpoint} {
position: relative;
width: 100%;
padding-right: $gutter / 2;
padding-left: $gutter / 2;
}
.col-#{$breakpoint}-auto {
@extend %grid-column-#{$breakpoint};
flex: 0 0 auto;
width: auto;
max-width: 100%;
}
@for $i from 1 through $columns {
.col-#{$breakpoint}-#{$i} {
@extend %grid-column-#{$breakpoint};
flex: 0 0 percentage($i / $columns);
max-width: percentage($i / $columns);
}
}
}
}
}
}
@include make-grid-columns;
构建工具优化
Webpack配置优化
javascript
// webpack.config.js
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
module: {
rules: [
{
test: /\.(scss|css)$/,
use: [
// 生产环境使用MiniCssExtractPlugin,开发环境使用style-loader
process.env.NODE_ENV === 'production' ? MiniCssExtractPlugin.loader : 'style-loader',
'css-loader',
{
loader: 'sass-loader',
options: {
implementation: require('sass'),
sassOptions: {
includePaths: [path.resolve(__dirname, 'src/scss')],
outputStyle: process.env.NODE_ENV === 'production' ? 'compressed' : 'expanded',
sourceMap: process.env.NODE_ENV !== 'production'
}
}
}
]
}
]
},
plugins: [
new MiniCssExtractPlugin({
filename: process.env.NODE_ENV === 'production' ? '[name].[contenthash].css' : '[name].css'
})
],
optimization: {
splitChunks: {
cacheGroups: {
styles: {
name: 'styles',
type: 'css/mini-extract',
chunks: 'all',
enforce: true
}
}
}
}
};
Gulp构建优化
javascript
// gulpfile.js
const gulp = require('gulp');
const sass = require('gulp-sass')(require('sass'));
const autoprefixer = require('gulp-autoprefixer');
const cleanCSS = require('gulp-clean-css');
const sourcemaps = require('gulp-sourcemaps');
// 开发构建
gulp.task('sass:dev', function() {
return gulp.src('src/scss/**/*.scss')
.pipe(sourcemaps.init())
.pipe(sass({
outputStyle: 'expanded',
includePaths: ['node_modules']
}).on('error', sass.logError))
.pipe(autoprefixer({
cascade: false
}))
.pipe(sourcemaps.write('.'))
.pipe(gulp.dest('dist/css'));
});
// 生产构建
gulp.task('sass:prod', function() {
return gulp.src('src/scss/**/*.scss')
.pipe(sass({
outputStyle: 'compressed',
includePaths: ['node_modules']
}).on('error', sass.logError))
.pipe(autoprefixer({
cascade: false
}))
.pipe(cleanCSS({
level: 2
}))
.pipe(gulp.dest('dist/css'));
});
// 监听文件变化
gulp.task('watch', function() {
gulp.watch('src/scss/**/*.scss', gulp.parallel('sass:dev'));
});
调试和分析
CSS分析工具
scss
// 调试混合宏
@mixin debug() {
outline: 1px solid red !important;
}
// 条件调试
$debug-mode: false !default;
@mixin conditional-debug() {
@if $debug-mode {
outline: 1px solid red !important;
}
}
// 使用调试混合宏
.debug-element {
@include conditional-debug;
padding: 1rem;
}
// 生成注释以帮助调试
@mixin with-comment($comment) {
/* #{$comment} */
@content;
}
.component {
@include with-comment('This is a main component');
padding: 1rem;
.sub-component {
@include with-comment('This is a sub component');
margin: 0.5rem;
}
}
性能监控
scss
// 创建性能基准测试
@function performance-test($function, $iterations: 1000) {
$start: 0;
$end: 0;
@for $i from 1 through $iterations {
$result: call($function);
}
@return $result;
}
// 优化的工具类生成
$utilities: (
'margin': (
'property': 'margin',
'values': $spacers
),
'padding': (
'property': 'padding',
'values': $spacers
),
'text-color': (
'property': 'color',
'values': $colors
)
);
@each $key, $utility in $utilities {
@each $pseudo, $value in map-get($utility, 'values') {
$property: map-get($utility, 'property');
.#{$key}-#{$pseudo} {
#{$property}: $value;
}
}
}
通过应用这些性能优化技术,您可以显著提高Sass的编译速度,减少CSS文件大小,并创建更高效的样式系统。