SCSS模块系统详解 @import、@use、@forward 深度解析

SCSS(Sass)提供了强大的模块系统来组织和管理样式代码。本文将详细介绍三个核心指令:@import@use@forward,帮助你构建更好的样式架构。

目录

  • @import - 传统的导入方式
  • @use - 现代模块系统
  • @forward - 模块转发
  • 最佳实践
  • 迁移指南

@import - 传统的导入方式

基本语法

@import "path/to/file";
@import "file1", "file2", "file3";
@import url("https://fonts.googleapis.com/css2?family=Roboto");

特点

  • 全局作用域:导入的所有变量、混合器、函数都变成全局可用
  • 重复导入问题:同一文件可能被多次导入,导致 CSS 重复
  • 命名冲突:没有命名空间,容易产生变量名冲突

示例

// _variables.scss
$primary-color: #007bff;
$font-size: 16px;

// _mixins.scss
@mixin button-style {
  padding: 10px 20px;
  border-radius: 4px;
}

// main.scss
@import "variables";
@import "mixins";

.button {
  @include button-style;
  background-color: $primary-color;
  font-size: $font-size;
}

问题与限制

// 问题示例:命名冲突
// _theme1.scss
$color: red;

// _theme2.scss
$color: blue;

// main.scss
@import "theme1";
@import "theme2";
// $color 现在是 blue,theme1 的值被覆盖了

@use - 现代模块系统

基本语法

@use "path/to/file";
@use "path/to/file" as namespace;
@use "path/to/file" as *;
@use "path/to/file" with (
  $variable: value
);

核心特性

1. 命名空间

// _colors.scss
$primary: #007bff;
$secondary: #6c757d;

@function lighten-color($color, $amount) {
  @return lighten($color, $amount);
}

// main.scss
@use "colors";

.button {
  background-color: colors.$primary;
  border-color: colors.lighten-color(colors.$primary, 20%);
}

2. 自定义命名空间

// 使用自定义命名空间
@use "colors" as c;

.button {
  background-color: c.$primary;
}

// 使用通配符导入到全局
@use "colors" as *;

.button {
  background-color: $primary; // 直接使用,无需命名空间
}

3. 配置变量

// _theme.scss
$base-font-size: 16px !default;
$primary-color: #007bff !default;

.text {
  font-size: $base-font-size;
  color: $primary-color;
}

// main.scss
@use "theme" with (
  $base-font-size: 18px,
  $primary-color: #28a745
);

4. 私有成员

// _utils.scss
$_private-var: "This is private";
$public-var: "This is public";

@function _private-function() {
  @return $_private-var;
}

@function public-function() {
  @return $public-var;
}

// main.scss
@use "utils";

.test {
  // content: utils.$public-var; ✓ 可以访问
  // content: utils.$_private-var; ✗ 错误:私有变量不可访问
  // content: utils.public-function(); ✓ 可以访问
  // content: utils._private-function(); ✗ 错误:私有函数不可访问
}

高级用法

条件导入

// 根据条件导入不同模块
@use "sass:meta";

@if meta.feature-exists("at-error") {
  @use "modern-utils";
} @else {
  @use "legacy-utils";
}

动态配置

// _config.scss
$theme: "light" !default;
$breakpoints: (
  "sm": 576px,
  "md": 768px,
  "lg": 992px,
) !default;

// main.scss
@use "config" with (
  $theme: "dark",
  $breakpoints: (
    "sm": 480px,
    "md": 768px,
    "lg": 1024px,
    "xl": 1200px,
  )
);

@forward - 模块转发

基本概念

@forward 允许一个模块加载另一个模块的成员,并将这些成员作为自己 API 的一部分导出。

基本语法

@forward "path/to/file";
@forward "path/to/file" hide $var1, function1;
@forward "path/to/file" show $var1, $var2;
@forward "path/to/file" as prefix-*;
@forward "path/to/file" with (
  $variable: value
);

实际应用场景

1. 创建统一入口文件

// _variables.scss
$primary-color: #007bff;
$secondary-color: #6c757d;

// _mixins.scss
@mixin button {
  padding: 10px 20px;
  border-radius: 4px;
}

@mixin card {
  border: 1px solid #dee2e6;
  border-radius: 8px;
}

// _functions.scss
@function rem($pixels) {
  @return #{$pixels / 16}rem;
}

// index.scss - 统一入口
@forward "variables";
@forward "mixins";
@forward "functions";

// main.scss - 使用者只需导入一个文件
@use "index" as theme;

.button {
  @include theme.button;
  background-color: theme.$primary-color;
  font-size: theme.rem(16);
}

2. 选择性转发

// _internal.scss
$_internal-var: "secret";
$public-var: "visible";
$debug-var: "debug-info";

@function _internal-helper() {
  /* ... */
}
@function public-helper() {
  /* ... */
}
@function debug-helper() {
  /* ... */
}

// _public-api.scss
// 只转发公共API,隐藏内部实现和调试工具
@forward "internal" hide $_internal-var, _internal-helper, $debug-var, debug-helper;

// 或者使用 show 明确指定要转发的成员
// @forward 'internal' show $public-var, public-helper;

3. 添加前缀避免冲突

// _bootstrap-vars.scss
$primary: #007bff;
$secondary: #6c757d;

// _material-vars.scss
$primary: #2196f3;
$surface: #ffffff;

// _unified-theme.scss
@forward "bootstrap-vars" as bs-*;
@forward "material-vars" as md-*;

// main.scss
@use "unified-theme" as theme;

.bootstrap-button {
  background-color: theme.$bs-primary;
}

.material-button {
  background-color: theme.$md-primary;
}

4. 配置转发

// _base-theme.scss
$font-family: "Helvetica Neue" !default;
$base-font-size: 16px !default;
$line-height: 1.5 !default;

// _dark-theme.scss
@forward "base-theme" with (
  $font-family: "Roboto",
  $base-font-size: 14px
);

// 添加暗色主题特定的变量
$background-color: #1a1a1a;
$text-color: #ffffff;

// main.scss
@use "dark-theme";

复杂示例:构建设计系统

// design-system/
// ├── tokens/
// │   ├── _colors.scss
// │   ├── _typography.scss
// │   ├── _spacing.scss
// │   └── index.scss
// ├── components/
// │   ├── _button.scss
// │   ├── _card.scss
// │   └── index.scss
// └── index.scss

// tokens/_colors.scss
$blue-50: #e3f2fd;
$blue-500: #2196f3;
$blue-900: #0d47a1;

$primary: $blue-500 !default;
$primary-light: $blue-50 !default;
$primary-dark: $blue-900 !default;

// tokens/_typography.scss
$font-family-base: "Roboto", sans-serif !default;
$font-size-base: 16px !default;
$font-weight-normal: 400 !default;
$font-weight-bold: 700 !default;

// tokens/_spacing.scss
$spacing-xs: 4px;
$spacing-sm: 8px;
$spacing-md: 16px;
$spacing-lg: 24px;
$spacing-xl: 32px;

// tokens/index.scss
@forward "colors";
@forward "typography";
@forward "spacing";

// components/_button.scss
@use "../tokens" as tokens;

.button {
  font-family: tokens.$font-family-base;
  font-size: tokens.$font-size-base;
  font-weight: tokens.$font-weight-bold;
  padding: tokens.$spacing-sm tokens.$spacing-md;
  background-color: tokens.$primary;
  color: white;
  border: none;
  border-radius: 4px;

  &:hover {
    background-color: tokens.$primary-dark;
  }
}

// components/index.scss
@forward "button";
@forward "card";

// design-system/index.scss
@forward "tokens";
@forward "components";

// 最终使用
// app.scss
@use "design-system" with (
  $primary: #ff5722,
  $font-family-base: "Inter"
);

最佳实践

1. 文件组织结构

styles/
├── abstracts/
│   ├── _variables.scss
│   ├── _functions.scss
│   ├── _mixins.scss
│   └── index.scss
├── base/
│   ├── _reset.scss
│   ├── _typography.scss
│   └── index.scss
├── components/
│   ├── _button.scss
│   ├── _card.scss
│   └── index.scss
├── layout/
│   ├── _header.scss
│   ├── _sidebar.scss
│   └── index.scss
└── main.scss

2. 命名约定

// 使用语义化的命名空间
@use "abstracts" as abs;
@use "components" as comp;
@use "layout" as layout;

// 私有成员使用下划线前缀
$_private-variable: value;
@function _private-function() {
  /* ... */
}

// 使用 !default 标记可配置变量
$primary-color: #007bff !default;

3. 避免深度嵌套

// ✗ 避免
@use "level1" as l1;
// level1 内部又 @use 了很多其他模块

// ✓ 推荐:使用 @forward 创建扁平的API
@forward "level1";
@forward "level2";
@forward "level3";

4. 性能优化

// 只导入需要的成员
@use "large-library" as lib;
// 只使用 lib.$specific-variable 而不是全部导入

// 使用条件导入
@use "sass:meta";
@if meta.module-exists("optional-feature") {
  @use "optional-feature";
}

迁移指南

从 @import 到 @use 的迁移步骤

1. 分析依赖关系

// 原始代码
@import "variables";
@import "mixins";
@import "functions";

// 迁移后
@use "variables" as vars;
@use "mixins" as mix;
@use "functions" as fn;

2. 更新变量引用

// 原始代码
.button {
  color: $primary-color;
  @include button-style;
  font-size: calculate-rem(16);
}

// 迁移后
.button {
  color: vars.$primary-color;
  @include mix.button-style;
  font-size: fn.calculate-rem(16);
}

3. 处理循环依赖

// 使用 @forward 解决循环依赖
// _api.scss
@forward "variables";
@forward "mixins";
@forward "functions";

// main.scss
@use "api";

4. 渐进式迁移

// 可以在同一项目中混用,但要避免同时导入同一文件
@import "legacy-file"; // 旧的导入方式
@use "new-file" as new; // 新的导入方式

总结

特性 @import @use @forward
命名空间
重复导入保护
配置变量
私有成员
性能 较差 良好 良好
推荐使用

选择指南

  • @use:在需要使用其他模块的功能时使用
  • @forward:在创建 API 入口或重新导出模块时使用
  • @import:仅在处理遗留代码或 CSS 文件时使用

通过合理使用这三个指令,你可以构建出更加模块化、可维护和高性能的 SCSS 代码架构。建议在新项目中完全采用 @use@forward,逐步将现有项目从 @import 迁移过来。