Skip to content
On this page

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文件大小,并创建更高效的样式系统。