阅读视图

发现新文章,点击刷新页面。

Sass与Less全面对比(含语法+场景+选型)

Sass(Syntactically Awesome Style Sheets)和 Less(Leaner Style Sheets)是目前最主流的两款CSS预处理器,二者核心目标一致——扩展CSS的功能,解决原生CSS无变量、无嵌套、无复用性等痛点,让样式开发更高效、代码更易维护。但两者在起源、语法细节、功能特性、生态支持等方面存在显著差异,选择时需结合项目规模、团队习惯和需求场景综合判断。

一、核心差异总览(表格清晰对比)

对比维度 Sass Less
起源与底层实现 2007年诞生,最初基于Ruby实现,目前官方推荐使用Dart Sass(更易维护、性能更优),Node Sass已停更废弃。 2009年诞生,基于JavaScript实现,依赖Node.js环境编译,学习门槛相对较低,可在浏览器端直接解析(不推荐生产环境)。
语法风格 支持两种语法:① SCSS(.scss后缀):兼容原生CSS,使用大括号和分号,目前最常用;② 缩进式(.sass后缀):无大括号和分号,靠缩进区分代码块,格式要求严格。 仅支持一种语法(.less后缀),完全兼容原生CSS,必须使用大括号和分号,写法与原生CSS高度一致,上手更轻松。
变量声明 使用$符号定义变量,支持!default设置默认值(仅在变量未定义时生效),作用域严格,局部变量不影响全局,重定义未加!default的变量会报错。 使用@符号定义变量,采用“懒求值”机制,同名变量后声明会覆盖前声明(无论是否在嵌套块内),局部变量可直接覆盖全局变量,无报错提示。
嵌套与父选择器 支持选择器嵌套,父选择器&解析严格,要求符号与选择器间无多余空格(如&:hover合法),带空格会编译报错,避免隐性错误。 支持选择器嵌套,父选择器&解析宽松,允许省略空格或多余空格(如&.disabled& .disabled均合法),易出现“编译成功但结果异常”的情况。
混合(Mixins) @mixin定义混合宏,@include调用,支持参数、默认值和可变参数,功能灵活,可配合逻辑控制实现复杂复用逻辑。 用类选择器(可加括号,加括号不输出到CSS)定义混合,直接通过类名调用,支持参数和默认值,功能相对基础,无复杂逻辑支持。
继承 @extend实现继承,支持“占位符选择器”(%开头),仅用于继承,不生成冗余CSS,复用效率更高。 @extend实现继承,但不支持占位符选择器,被继承的类会被编译到最终CSS中,易产生冗余代码。
逻辑控制 支持完整的逻辑控制:@if/@else条件判断、@for/@each/@while循环,还可通过@function自定义函数,适合复杂动态样式生成。 逻辑控制能力较弱,仅支持简单的when条件判断和递归循环(需手动终止),无原生自定义函数功能,复杂逻辑需通过混合模拟。
模块化机制 采用现代化模块系统,通过@use(直接使用模块)和@forward(转发模块成员)实现模块化,自动单例加载,支持命名空间和私有成员,彻底避免命名冲突和重复加载。 依赖@import实现模块化,无命名空间和单例加载机制,多次导入同一文件会重复编译,易造成全局污染和代码冗余,无原生私有成员支持。
内置函数 内置函数丰富,涵盖颜色处理、字符串操作、数学计算等,支持颜色对象运算,类型安全,边界值处理更严谨(如纯黑颜色调整),还可通过内置模块(如sass:math、sass:color)扩展功能。 内置函数相对基础,主要支持简单的颜色调整(如lighten、darken),函数参数和返回值类型不统一,颜色操作仅支持字符串拼接,无法参与复杂运算,易出现解析异常。
生态与框架支持 生态更成熟,社区活跃,插件丰富,主流框架(Bootstrap 4+、Angular、Vue CLI)均优先支持,与Webpack、Vite等构建工具集成流畅,Dart Sass编译速度快,适合大型项目。 生态相对小众,早期用于Bootstrap 3,目前在部分企业级老项目中仍有应用,与构建工具集成时存在配置限制(如Vite不支持javascriptEnabled配置),适合小型项目或老项目维护。
学习门槛 中等,SCSS语法兼容CSS,基础用法易上手,但高级特性(逻辑控制、模块化)需额外学习,缩进式语法对格式要求较高。 低,语法完全贴近原生CSS,无额外格式要求,基础功能简单易懂,适合刚接触预处理器的开发者快速上手。

二、核心语法对比(附代码示例)

以下针对最常用的核心功能,对比两者的语法差异,所有示例均采用各自最主流的语法(Sass用SCSS,Less用默认语法)。

1. 变量声明与使用

// 定义全局变量,设置默认值(未定义时生效)
$primary-color: #2563eb !default;
$font-size: 16px;

// 局部变量(仅在.box内生效,不影响全局)
.box {
  $local-color: #6c757d;
  color: $primary-color; // 全局变量:#2563eb
  background: $local-color; // 局部变量:#6c757d
  font-size: $font-size; // 全局变量:16px
}

// 重定义带!default的变量(合法,覆盖默认值)
$primary-color: #1d4ed8;
.text {
  color: $primary-color; // 覆盖后:#1d4ed8
}
// 定义全局变量
@primary-color: #2563eb;
@font-size: 16px;

// 局部变量(覆盖全局变量)
.box {
  @primary-color: #6c757d;
  color: @primary-color; // 局部变量:#6c757d(覆盖全局)
  font-size: @font-size; // 全局变量:16px
}

// 重定义变量(直接覆盖,无报错)
@primary-color: #1d4ed8;
.text {
  color: @primary-color; // 覆盖后:#1d4ed8
}

2. 选择器嵌套与父选择器

.nav {
  width: 100%;
  height: 60px;
  
  // 子选择器嵌套
  > li {
    float: left;
    margin: 0 10px;
    
    // 父选择器&(严格解析,无空格)
    &:hover {
      color: $primary-color;
    }
    &.active {
      font-weight: bold;
    }
  }
}
// 编译后无冗余,&解析准确
.nav {
  width: 100%;
  height: 60px;
  
  // 子选择器嵌套(与Sass一致)
  > li {
    float: left;
    margin: 0 10px;
    
    // 父选择器&(宽松解析,允许空格)
    & :hover { // 多余空格,编译为.nav li :hover(非预期)
      color: @primary-color;
    }
    &.active {
      font-weight: bold;
    }
  }
}
// 易因空格问题导致样式异常,需格外注意

3. 混合(Mixins)用法

// 定义带参数、默认值的混合
@mixin flex-center($direction: row) {
  display: flex;
  flex-direction: $direction;
  justify-content: center;
  align-items: center;
}

// 调用混合(传递参数)
.box {
  @include flex-center(column);
  width: 300px;
  height: 200px;
}

// 调用混合(使用默认值)
.card {
  @include flex-center;
  background: #fff;
}
// 定义带参数、默认值的混合(加括号不输出到CSS)
.flex-center(@direction: row) {
  display: flex;
  flex-direction: @direction;
  justify-content: center;
  align-items: center;
}

// 调用混合(直接使用类名,传递参数)
.box {
  .flex-center(column);
  width: 300px;
  height: 200px;
}

// 调用混合(使用默认值)
.card {
  .flex-center;
  background: #fff;
}

4. 继承用法

// 占位符选择器(仅用于继承,不输出到CSS)
%button-base {
  padding: 8px 16px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

// 继承占位符样式
.primary-btn {
  @extend %button-base;
  background: $primary-color;
  color: #fff;
}

// 继承占位符样式
.success-btn {
  @extend %button-base;
  background: #16a34a;
  color: #fff;
}
// 编译后无%button-base相关样式,无冗余
// 普通类选择器(会被编译到CSS中)
.button-base {
  padding: 8px 16px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

// 继承普通类样式
.primary-btn {
  @extend .button-base;
  background: @primary-color;
  color: #fff;
}

// 继承普通类样式
.success-btn {
  @extend .button-base;
  background: #16a34a;
  color: #fff;
}
// 编译后会保留.button-base样式,产生冗余

5. 逻辑控制用法

// 条件判断
$theme: dark;
.box {
  @if $theme == dark {
    background: #111;
    color: #fff;
  } @else {
    background: #fff;
    color: #333;
  }
}

// 循环(生成col-1到col-4)
@for $i from 1 to 5 {
  .col-#{$i} {
    width: 100% / $i;
    float: left;
  }
}
// 条件判断(通过when)
@theme: dark;
.box when (@theme = dark) {
  background: #111;
  color: #fff;
}
.box when not (@theme = dark) {
  background: #fff;
  color: #333;
}

// 递归循环(需手动终止)
.loop(@n) when (@n > 0) {
  .col-@{n} {
    width: 100% / @n;
    float: left;
  }
  .loop(@n - 1); // 递归调用,直到@n<=0
}
.loop(4); // 生成col-4到col-1

6. 模块化导入用法

// 1. 拆分模块:_variables.scss(局部文件,下划线开头不单独编译)
$primary-color: #2563eb;
$-private-var: 10px; // 私有成员(-/_开头,外部无法访问)

// 2. 主文件导入:main.scss
@use "./variables"; // 默认命名空间:variables,单例加载
@use "./variables" as v; // 自定义命名空间:v(重复导入仅加载一次)

.box {
  color: variables.$primary-color; // 通过命名空间访问
  padding: v.$primary-color;
  // margin: variables.$-private-var; // 报错:私有成员无法访问
}

// 3. 转发模块(供其他文件使用)
@forward "./variables" as var-*; // 转发所有成员,加前缀var-
// 1. 拆分模块:variables.less
@primary-color: #2563eb;
@private-var: 10px; // 无私有成员,外部可直接访问

// 2. 主文件导入:main.less
@import "./variables.less"; // 无命名空间,全局注入
@import "./variables.less"; // 重复导入,重复编译,产生冗余

.box {
  color: @primary-color; // 直接访问,无隔离
  padding: @private-var; // 无私有限制,可访问所有成员
}

三、编译环境与工具集成

1. Sass 编译环境

  • 主流实现:目前推荐使用Dart Sass(npm安装:npm install -g sass),替代已废弃的Node Sass,编译速度快、兼容性好,支持所有新特性。
  • 工具集成:与Webpack(sass-loader)、Vite(内置支持)、VS Code(Live Sass Compiler插件)集成流畅,支持source map调试,additionalData选项可注入全局变量,支持函数回调配置。
  • 编译命令:sass input.scss output.css(实时监听:sass --watch input.scss:output.css)。

2. Less 编译环境

  • 安装方式:基于Node.js,npm安装:npm install -g less,可通过less.js在浏览器端直接解析(仅适合开发调试,生产环境不推荐)。
  • 工具集成:与Webpack(less-loader)、Vite(内置支持)集成,但存在配置限制(如Vite不支持javascriptEnabled: true,无法运行JS表达式),additionalData选项仅支持字符串配置,不支持函数回调。
  • 编译命令:lessc input.less output.css(实时监听需借助第三方工具)。

四、项目选型建议

选型核心:结合项目规模、团队技术栈、功能需求,而非单纯追求“更强大”,优先保证开发效率和可维护性。

1. 优先选择 Sass(SCSS)的场景

  • 中大型项目/团队协作:需要复杂逻辑控制(如动态主题、批量样式生成)、严格的模块化隔离,避免命名冲突,Sass的@use/@forward、私有成员、逻辑控制等特性可大幅提升可维护性和协作效率。
  • 新项目开发:追求长期可维护性,希望适配主流技术生态,Sass的社区支持更完善、框架兼容性更好,后续扩展更便捷,是目前官方和行业推荐的首选方案。
  • 需要丰富的内置函数和高级特性:如复杂颜色处理、自定义函数、灵活的变量配置(!default),适合搭建设计系统或多主题项目,Sass的类型安全和函数链式调用更稳定可靠。
  • 使用主流前端框架:如Bootstrap 4+、Angular、Vue 3,这些框架均优先支持Sass,集成更流畅,减少配置成本。

2. 优先选择 Less 的场景

  • 小型项目/快速原型开发:需求简单,仅需变量、嵌套、基础混合等功能,Less语法贴近原生CSS,上手快、配置简单,可快速完成开发任务。
  • 维护旧项目:项目已基于Less开发,短期内无法迁移,继续使用Less可降低迁移成本,避免影响项目正常运行,Less的兼容性可保证旧代码稳定编译。
  • 团队成员不熟悉预处理器:团队以原生CSS开发为主,Less学习成本低,无需额外学习复杂语法,可快速过渡到预处理器开发模式。
  • 简单的浏览器端调试需求:Less可通过less.js直接在浏览器端解析,无需搭建复杂的编译环境,适合快速调试样式

五、常见问题与避坑指南

实际开发中,无论是Sass还是Less,都容易遇到语法、编译或集成相关的问题,以下梳理高频坑点及解决方案,帮助规避不必要的麻烦。

1. Sass 常见避坑点

  • 坑点1:混淆Node Sass与Dart Sass,导致编译报错。解决方案:彻底卸载Node Sass(npm uninstall node-sass),安装Dart Sass(npm install sass),确保项目依赖中无node-sass,避免版本冲突。
  • 坑点2:@use导入路径错误,提示“找不到模块”。解决方案:导入时省略下划线和文件后缀(如导入_variables.scss,写@use "./variables"),路径以当前文件为基准,避免绝对路径,跨目录导入需正确拼接相对路径(如@use "../utils/variables")。
  • 坑点3:误将SCSS语法用在缩进式Sass文件中,导致编译失败。解决方案:统一项目语法风格,优先使用SCSS(.scss后缀),若使用缩进式Sass(.sass后缀),需严格遵循“无大括号、无分号、靠缩进区分代码块”的规则。
  • 坑点4:重定义未加!default的变量,导致报错。解决方案:全局公共变量建议加!default(方便后续覆盖),局部变量仅在当前模块内使用,避免与全局变量重名,若需重定义全局变量,确保先导入变量文件,再重定义。

2. Less 常见避坑点

  • 坑点1:父选择器&添加多余空格,导致样式解析异常。解决方案:严格控制&与后续选择器的空格(如&:hover而非& :hover),避免编译后生成非预期的选择器(如.nav li :hover)。
  • 坑点2:Vite项目中启用javascriptEnabled: true,导致编译报错。解决方案:Vite内置的Less编译器不支持该配置,若需运行JS表达式,可改用Webpack+less-loader,或避免在Less中写入JS逻辑。
  • 坑点3:多次导入同一文件,导致CSS冗余。解决方案:尽量减少重复导入,可将公共模块(如变量、混合)集中在一个入口文件导入,再引入该入口文件,避免多文件重复导入同一模块。
  • 坑点4:变量覆盖导致样式异常,难以排查。解决方案:规范变量命名(如加模块前缀@btn-primary-color),避免全局变量与局部变量重名,复杂项目可按模块拆分变量文件,减少覆盖风险。

3. 通用避坑点

  • 避免嵌套过深:无论是Sass还是Less,嵌套层级建议不超过3层,否则会编译出冗长的选择器,影响CSS性能,且不利于代码维护。
  • 规范文件命名:局部模块文件(不单独编译的文件)建议以下划线开头(如_variables.scss_mixins.less),区分全局入口文件,避免编译生成多余的CSS文件。
  • 慎用!important:预处理器中尽量避免使用!important,若需提高样式优先级,可通过调整选择器权重(如增加父选择器)实现,否则会导致样式优先级混乱,难以调试。

六、实战对比总结

Sass和Less本质上都是为了解决原生CSS的痛点,提升样式开发效率,但两者的定位和适用场景有明显区分,无需纠结“谁更好”,只需结合自身需求选择即可,核心总结如下:

  • 从功能强大度来看:Sass > Less,Sass的模块化、逻辑控制、内置函数等高级特性,更适合复杂项目和设计系统搭建,能解决更多场景下的开发痛点。
  • 从学习成本来看:Less < Sass,Less语法与原生CSS高度一致,上手门槛极低,适合新手或原生CSS开发者快速过渡,Sass的高级特性需要额外投入时间学习。
  • 从生态和未来趋势来看:Sass更具优势,官方持续更新维护,主流框架和构建工具优先支持,Node Sass的废弃也推动了Dart Sass的普及,而Less生态相对停滞,仅适合维护旧项目或小型项目。
  • 从团队协作来看:Sass更适合团队协作,严格的作用域、命名空间和私有成员机制,能有效避免命名冲突,清晰的依赖关系也便于代码维护和迭代;Less无模块化隔离,大型团队协作易出现问题。

最后补充一句:无论是选择Sass还是Less,核心是“规范使用”——统一语法风格、合理拆分模块、规范变量命名,才能真正发挥预处理器的优势,让样式代码更高效、更易维护。如果是新建项目,优先选择Sass(SCSS),贴合行业主流;如果是维护旧项目或快速开发,Less也是不错的选择。

七、快速选型对照表(便捷参考)

项目/团队情况 推荐选择 核心原因
中大型项目、团队协作 Sass(SCSS) 模块化强、无命名冲突、支持复杂逻辑,可维护性高
小型项目、快速原型开发 Less 上手快、配置简单,满足基础需求,开发效率高
新建项目、追求长期维护 Sass(SCSS) 生态成熟、官方推荐,适配主流框架,扩展便捷
旧项目维护(基于Less) Less 降低迁移成本,保证旧代码稳定编译,无需额外学习
新手开发者、原生CSS过渡 Less 语法贴近原生CSS,学习成本低,快速上手无压力
搭建设计系统、多主题项目 Sass(SCSS) 内置函数丰富、变量配置灵活,支持复杂动态样式生成

SCSS中@use与@import的区别

SCSS(Sassy CSS)中@use@import均用于实现样式模块化,实现代码复用,但二者在作用域、加载机制、命名空间等核心特性上差异显著。其中@import是SCSS早期的导入语法,存在全局污染、重复加载等问题,而@use是Sass 3.8+推出的新版模块化语法,旨在解决@import的缺陷,目前已被官方推荐作为首选导入方式,未来@import将逐步被弃用。

一、核心差异对比(表格清晰呈现)

对比维度 @import(旧版语法) @use(新版推荐)
作用域 全局作用域,导入的变量、混合宏(mixin)、函数会直接注入当前文件的全局作用域,易造成命名冲突和变量污染。 局部作用域,导入的内容被封装在独立模块中,需通过命名空间访问,从根本上避免全局污染和命名冲突。
加载机制 多次导入同一文件时,会重复加载、重复编译,增加编译时间,可能导致输出CSS冗余。 自动实现单例加载,无论导入多少次同一文件,仅加载、编译一次,提升编译效率,避免冗余代码。
命名空间 无命名空间,导入的所有成员(变量、mixin等)可直接访问,无需前缀,易引发命名冲突,需通过冗长命名规避冲突。 默认以导入文件的文件名作为命名空间,也可自定义命名空间;访问成员时需加上命名空间前缀,可通过as *省略前缀(慎用)。
私有成员支持 不支持私有成员,导入文件中所有定义的变量、mixin均可被外部访问,无法实现成员隐藏。 支持私有成员,以-_为前缀的变量、mixin视为私有,仅能在定义文件内部使用,外部无法访问,实现更好的封装性。
依赖关系 依赖关系混乱,无法清晰判断变量、mixin的来源,不利于大型项目维护和团队协作。 依赖关系显式化,通过命名空间可明确知道每个成员的来源,代码可维护性大幅提升,适合大型项目和团队协作。
变量配置 通过重定义变量覆盖默认值(需在@import前定义),但全局变量易被意外修改,配置逻辑不清晰。 支持通过with语句针对性配置模块变量,不影响全局,配置逻辑更严谨、可追溯。
官方支持 已被官方不推荐使用,计划逐步弃用,仅为兼容旧代码保留,部分新特性不支持。 官方推荐首选语法,支持所有新特性,是SCSS模块化开发的标准方案,与@forward配合实现更完善的模块化体系。

上述表格已清晰列出@use@import的所有核心区别,接下来我们重点拆解最影响开发效率和代码质量的两个特性——重复加载和命名空间。

二、重点特性详解(重复加载+命名空间)

2.1 重复加载(性能与冗余核心差异)

重复加载是@import最突出的问题之一,会直接影响样式文件性能和代码冗余度,而@use通过单例加载机制完美解决了这一问题。

  • 当使用 @import 导入模块时,如果在多个文件中多次导入同一个文件,会导致重复加载的问题。
  • 这意味着被导入的文件将在每个使用了 @import 的文件中都被加载一次,导致样式表中包含多份相同的样式,从而影响性能和增加文件大小。

我们通过一个实际示例,直观感受重复加载的问题:

// _variables.scss(被重复导入的模块)
$primary-color: #007bff;
$secondary-color: #6c757d;

// styles1.scss
@import 'variables';
body {
  background-color: $primary-color;
}

// styles2.scss
@import 'variables';
button {
  background-color: $secondary-color;
}

示例解析:我们有两个样式文件 styles1.scss 和 styles2.scss,它们分别使用 @import 导入了同一个 _variables.scss 文件。由于 styles1.scss 和 styles2.scss 都导入了 _variables.scss,在编译这两个样式文件时,_variables.scss 将被加载两次。

编译后的结果如下所示:

// 编译后的 styles1.cssbody {
  background-color: #007bff;
}

// 编译后的 styles2.cssbutton {
  background-color: #6c757d;
}

可以看到,虽然编译后的CSS中未直接显示重复的变量定义,但 _variables.scss 中的内容在编译过程中被加载了两次,不仅增加了编译时间,若模块中包含实际样式(而非仅变量),会导致CSS文件中出现多份相同样式,增加文件大小、影响页面加载性能,还可能引发潜在的样式冲突。

而使用 @use 导入方式可以避免重复加载问题,因为它会确保每个模块只加载一次,即使在多个文件中导入。这样可以优化编译性能,并保持样式表的精简和一致性。

2.2 命名空间(避免冲突的核心机制)

@import 没有命名空间机制,这是导致其命名冲突的核心原因;而 @use 内置命名空间功能,可灵活隔离模块成员,提升代码可读性和可维护性,具体分为三种使用场景。

2.2.1 不使用as:直接以文件名作为命名空间

当在 @use 后面直接跟上文件路径,且不使用 as 关键字指定命名空间时,会将导入的模块整体作为一个命名空间,且使用被导入文件的名称作为命名空间标识(省略下划线前缀)。

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

// styles.scss
@use 'variables.scss';  // 省略下划线,默认命名空间为variables
body {   
  background-color: variables.$primary-color; // 通过命名空间访问变量
}  
button {   
  background-color: variables.$secondary-color;
}

示例解析:styles.scss 使用 @use 直接导入了 variables.scss 文件,未指定自定义命名空间,因此 _variables.scss 中的所有内容被封装在 variables 命名空间下,访问时需加上 variables.前缀,避免与当前文件的其他变量冲突。

2.2.2 使用as xxx:自定义命名空间

通过 as 关键字可以为导入的模块自定义命名空间,让命名更简洁、贴合业务场景,进一步提升代码可读性。

// _variables.scss(与上例一致)
$primary-color: #007bff;
$secondary-color: #6c757d; 

// styles.scss
@use 'variables.scss' as customVars;  // 自定义命名空间为customVars
body {   
  background-color: customVars.$primary-color; 
}  
button {   
  background-color: customVars.$secondary-color; 
}

示例解析:通过 as customVars 为导入的模块创建了自定义命名空间 customVars,后续访问模块中的变量时,需通过 customVars. 前缀,既避免了命名冲突,又让变量来源更清晰。

2.2.3 特殊情况:使用as * 导入(无命名空间)

如果在 @use 后面使用 as *,表示将导入的模块的所有内容直接合并到当前文件中,不创建任何命名空间,导入的变量、mixin、函数等可直接使用。

// _variables.scss(与上例一致)
$primary-color: #007bff;
$secondary-color: #6c757d;

// styles.scss
@use 'variables.scss' as *;  // 无命名空间,直接合并内容
body {   
  background-color: $primary-color; // 直接访问变量,无需前缀
}  
button {   
  background-color: $secondary-color;
}

注意:这种方式会丧失命名空间的隔离优势,与 @import 类似,易引发命名冲突,仅建议在变量统一管理、无冲突风险的简单场景使用。

三、语法用法补充(其他核心用法)

3.1 @import 其他常见问题

除了重复加载,@import 还存在全局污染、依赖混乱等问题,以下是基础用法回顾及问题总结:

// _variables.scss
$color: red;
$font-size: 16px;

// _utils.scss
$color: blue; // 与variables.scss中的$color重名
@mixin flex-center {
  display: flex;
  justify-content: center;
  align-items: center;
}

// main.scss
@import "./variables";
@import "./utils"; // 重名变量被覆盖
@import "./variables"; // 重复加载,增加编译冗余

.box {
  color: $color; // 输出blue(被utils.scss中的$color覆盖,意外污染)
  font-size: $font-size; // 输出16px
  @include flex-center;
}

编译后CSS:

.box {
  color: blue;
  font-size: 16px;
  display: flex;
  justify-content: center;
  align-items: center;
}

问题总结:@import导入的变量会全局覆盖,重复导入同一文件会重复编译,无法区分成员来源,维护难度高。

3.2 @use 其他优势用法

除了命名空间和单例加载,@use 还支持私有成员、变量配置等优势特性,以下是补充示例:

// _theme.scss
$-private-var: 10px; // 私有变量(仅文件内可用,前缀-/_)
$primary-color: #3498db !default; // 默认变量,可被配置覆盖
$secondary-color: #2ecc71 !default;

// main.scss
// 自定义命名空间为t,并通过with配置变量
@use "./theme" as t with (
  $primary-color: #e74c3c, // 覆盖默认主色
  $secondary-color: #f39c12
);

.box {
  background: t.$primary-color; // 输出#e74c3c(配置后的值)
  color: t.$secondary-color; // 输出#f39c12(配置后的值)
  // margin: t.$-private-var; // 报错:私有变量无法访问
}

编译后CSS:

.box {
  background: #e74c3c;
  color: #f39c12;
}

四、实际开发场景选择建议

1. 优先使用@use的场景

  • 新建SCSS项目:全程使用@use,配合@forward实现模块化拆分(如按变量、mixin、组件拆分文件),提升代码可维护性和可扩展性。
  • 大型项目/团队协作:通过命名空间隔离和显式依赖,避免命名冲突,清晰区分成员来源,降低协作成本和维护难度。
  • 需要封装私有成员:当部分变量、mixin仅需在当前文件使用,无需暴露给外部时,使用@use的私有成员特性,实现代码封装。
  • 使用Sass新特性:@use支持所有Sass新特性(如内置模块导入),而@import不支持部分新特性,无法适配未来升级需求。

2. 临时使用@import的场景

  • 维护旧项目:当项目中大量使用@import,短期内无法全部迁移时,可临时保留,逐步替换为@use,避免影响项目正常运行。
  • 导入纯CSS文件:虽然@use也可导入纯CSS文件,但@import语法更简洁,且无需处理命名空间(仅适用于简单场景)。

五、补充注意事项

  • @use导入文件时,可省略文件扩展名(.scss、.sass),也可省略下划线前缀(如导入_variables.scss,可写为@use "./variables")。
  • @use的as *语法可省略命名空间,直接访问导入的成员,但会丧失命名空间的隔离优势,易引发冲突,仅建议在变量统一管理的简单场景使用。
  • 迁移旧项目时,需注意:@use中以-_开头的变量视为私有,若旧代码中存在此类命名的变量,导入后会无法访问,需修改变量命名或调整访问方式。
  • @use与@forward的配合:@forward用于转发模块成员(不直接使用),适合库开发或入口文件整合;@use用于直接使用模块成员,二者配合可实现更灵活的模块化体系。
  • Sass内置模块(如sass:math、sass:color)需通过@use导入才能使用,无法通过@import导入,这也是官方推荐@use的重要原因之一。

六、总结

推荐使用 @use 来导入模块,以获得更好的模块化支持、性能优化和避免全局污染问题。其核心优势在于局部作用域、命名空间隔离、单例加载、私有成员支持,彻底解决了@import的全局污染、重复加载、依赖混乱等问题,提升了代码的可维护性、可扩展性和协作效率。而 @import 在新版本 Sass 中已不再推荐使用,并且未来可能会被废弃,仅适合临时维护旧项目,新建项目或项目升级时,应优先采用@use + @forward的模块化方案,遵循官方推荐的开发规范,避免后续维护成本增加。

学习Less,看这篇就够了(从入门到实战)

Less(Leaner Style Sheets)是CSS预处理器,在原生CSS基础上增加变量、嵌套、混合、函数、运算、模块化等编程特性,让CSS更易维护、复用、扩展,最终编译成标准CSS运行。本文从环境搭建、核心语法、进阶技巧、实战规范全流程覆盖,新增多个优雅使用案例(含Less源码与编译后CSS对比),直接上手可用。

一、Less基础:是什么、为什么、怎么用

1.1 核心优势(为什么用Less)

  • 变量统一管理:颜色、尺寸、字体等全局配置,一改全改
  • 嵌套结构:完全匹配HTML层级,代码更直观、减少重复选择器
  • 混合(Mixin):复用样式片段,像函数一样传参
  • 运算/函数:支持加减乘除、颜色明暗、单位转换
  • 模块化:拆分文件、导入合并,便于团队协作与维护
  • 兼容原生CSS:所有CSS代码可直接写在Less中,零学习门槛

1.2 环境搭建(3种方式,最快1分钟)

方式1:浏览器直接运行(学习/原型)


<!-- 1. 引入Less文件,rel必须是stylesheet/less -->
<link rel="stylesheet/less" type="text/css" href="styles.less" />
<!-- 2. 引入Less编译器(CDN,已替换为可用链接) -->
<script src="https://cdn.bootcdn.net/ajax/libs/less.js/4.2.0/less.min.js"></script>

⚠️ 仅适合开发调试,生产环境禁止使用(性能差、依赖JS);原CDN链接报错“link dead”,已替换为稳定可用版本

方式2:VSCode自动编译(推荐,日常开发)

  1. 安装插件:Easy LESS
  2. 新建.less文件,保存时自动生成同名.css
  3. 配置(可选,settings.json):

"less.compile": {
  "out": "../css/", // 输出到css文件夹
  "compress": true, // 压缩CSS
  "sourceMap": false
}

方式3:命令行编译(项目构建)

  1. 安装Node.js,全局安装Less:

npm install -g less
lessc -v # 验证安装
  1. 编译命令:

lessc styles.less styles.css # 基础编译
lessc styles.less styles.min.css --compress # 压缩输出

二、Less核心语法(必掌握,直接套用)

2.1 变量(@变量名:值)—— 统一管理,一改全改

变量以@开头,可存颜色、尺寸、字体、路径等,支持插值(选择器、属性、URL)。


// 1. 基础变量定义(语义化命名,便于维护)
@primary: #2563eb; // 主色
@success: #16a34a; // 成功色
@font-size: 16px; // 基础字体
@spacing: 20px; // 基础间距
@img-path: "../images"; // 图片路径

// 2. 变量使用(结合运算,减少硬编码)
.btn {
  background: @primary;
  font-size: @font-size;
  padding: @spacing/2; // 10px,无需手动计算
  margin: @spacing;
}

// 3. 变量插值(复用选择器、属性、URL,避免重复书写)
@selector: card;
@prop: width;
.@{selector} { // 编译为 .card
  @{prop}: 300px; // 编译为 width:300px
  background: url("@{img-path}/bg.png");
}

编译后CSS:


.btn {
  background: #2563eb;
  font-size: 16px;
  padding: 10px;
  margin: 20px;
}
.card {
  width: 300px;
  background: url("../images/bg.png");
}

优雅要点:变量语义化命名,通过插值复用选择器和路径,运算替代硬编码,后续修改主色、间距时,仅需修改变量值,无需逐行修改样式。

2.2 嵌套规则(&父选择器)—— 匹配HTML结构,告别重复

Less允许选择器嵌套,&代表当前父选择器,用于伪类、交集选择器、兄弟选择器,避免重复书写父选择器。


// HTML结构:<div class="header"><nav class="nav"><a href="#" class="active">首页</a></nav></div>
.header {
  width: 100%;
  height: 60px;
  background: #fff;
  box-shadow: 0 2px 8px rgba(0,0,0,0.1); // 增加阴影,提升质感
  .nav {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100%;
    a {
      color: #333;
      text-decoration: none;
      padding: 0 @spacing/2; // 复用间距变量
      margin: 0 @spacing;
      // & 代表父选择器 .nav a,避免书写 .nav a:hover
      &:hover { 
        color: @primary;
        transition: color 0.3s ease; // 过渡效果,更优雅
      }
      &.active { 
        font-weight: bold; 
        color: @primary;
        border-bottom: 2px solid @primary;
      }
    }
  }
}

编译后CSS(对比:选择器自动拼接,无需手动重复):


.header {
  width: 100%;
  height: 60px;
  background: #fff;
  box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.header .nav {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100%;
}
.header .nav a {
  color: #333;
  text-decoration: none;
  padding: 0 10px;
  margin: 0 20px;
}
.header .nav a:hover {
  color: #2563eb;
  transition: color 0.3s ease;
}
.header .nav a.active {
  font-weight: bold;
  color: #2563eb;
  border-bottom: 2px solid #2563eb;
}

优雅要点:嵌套结构与HTML完全对应,可读性极强;&的使用避免重复书写父选择器(如.header .nav a),同时结合过渡效果,提升交互质感。

2.3 混合(Mixin)—— 复用样式,支持传参(核心)

Mixin是可复用的样式片段,带()不输出到CSS,不带()会输出;支持参数、默认值、条件判断,可封装公共样式,避免重复编码。

(1)基础Mixin(无参)—— 封装公共样式


// 定义:清除浮动(公共Mixin,可全局调用)
.clearfix() {
  &::after {
    content: "";
    display: block;
    clear: both;
    height: 0;
    visibility: hidden;
  }
}

// 定义:居中布局(公共Mixin,复用性强)
.center() {
  display: flex;
  justify-content: center;
  align-items: center;
}

// 使用:多个容器复用,无需重复书写样式
.container {
  .clearfix(); // 调用清除浮动
  width: 1200px;
  margin: 0 auto;
}
.modal {
  .center(); // 调用居中布局
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0,0,0,0.5);
}

编译后CSS(对比:Mixin样式自动注入,无需重复书写):


.container {
  width: 1200px;
  margin: 0 auto;
}
.container::after {
  content: "";
  display: block;
  clear: both;
  height: 0;
  visibility: hidden;
}
.modal {
  display: flex;
  justify-content: center;
  align-items: center;
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0,0,0,0.5);
}

(2)带参数Mixin(默认值、多参数)—— 动态生成样式


// 定义:按钮样式(带参数+默认值,灵活适配不同场景)
.btn(@bg: @primary, @color: #fff, @radius: 4px, @padding: 8px 16px) {
  display: inline-block;
  padding: @padding;
  background: @bg;
  color: @color;
  border-radius: @radius;
  cursor: pointer;
  border: none;
  outline: none;
  transition: all 0.3s ease; // 统一过渡效果
  &:hover { 
    filter: brightness(0.9); // hover变暗,无需单独写样式
    transform: translateY(-2px); // 轻微上浮,提升交互
  }
  &:active {
    transform: translateY(0);
  }
}

// 使用:按需传参,无需重复书写按钮基础样式
.btn-primary { .btn(); } // 使用默认值(主色按钮)
.btn-success { .btn(@success); } // 传单个参数(成功色按钮)
.btn-round { .btn(@primary, #fff, 50%, 10px 20px); } // 传全部参数(圆形按钮)
.btn-small { .btn(@primary, #fff, 4px, 4px 8px); } // 传部分参数(小尺寸按钮)

编译后CSS(对比:自动生成不同样式的按钮,代码简洁):


.btn-primary {
  display: inline-block;
  padding: 8px 16px;
  background: #2563eb;
  color: #fff;
  border-radius: 4px;
  cursor: pointer;
  border: none;
  outline: none;
  transition: all 0.3s ease;
}
.btn-primary:hover {
  filter: brightness(0.9);
  transform: translateY(-2px);
}
.btn-primary:active {
  transform: translateY(0);
}

.btn-success {
  display: inline-block;
  padding: 8px 16px;
  background: #16a34a;
  color: #fff;
  border-radius: 4px;
  cursor: pointer;
  border: none;
  outline: none;
  transition: all 0.3s ease;
}
.btn-success:hover {
  filter: brightness(0.9);
  transform: translateY(-2px);
}
.btn-success:active {
  transform: translateY(0);
}

.btn-round {
  display: inline-block;
  padding: 10px 20px;
  background: #2563eb;
  color: #fff;
  border-radius: 50%;
  cursor: pointer;
  border: none;
  outline: none;
  transition: all 0.3s ease;
}
.btn-round:hover {
  filter: brightness(0.9);
  transform: translateY(-2px);
}
.btn-round:active {
  transform: translateY(0);
}

.btn-small {
  display: inline-block;
  padding: 4px 8px;
  background: #2563eb;
  color: #fff;
  border-radius: 4px;
  cursor: pointer;
  border: none;
  outline: none;
  transition: all 0.3s ease;
}
.btn-small:hover {
  filter: brightness(0.9);
  transform: translateY(-2px);
}
.btn-small:active {
  transform: translateY(0);
}

(3)命名空间(组织Mixin,避免冲突)


// 命名空间:统一管理公共Mixin,避免与业务样式冲突
#utils() { // 带(),不输出到CSS
  .clearfix() { 
    &::after {
      content: "";
      display: block;
      clear: both;
      height: 0;
      visibility: hidden;
    }
  }
  .center() { 
    display: flex;
    justify-content: center;
    align-items: center; 
  }
  .shadow() { // 新增阴影Mixin
    box-shadow: 0 2px 12px rgba(0,0,0,0.1);
  }
}

// 调用:通过命名空间调用,清晰区分公共样式与业务样式
.box { 
  #utils.center(); 
  #utils.shadow();
  width: 300px;
  height: 200px;
  background: #fff;
}

编译后CSS:


.box {
  display: flex;
  justify-content: center;
  align-items: center;
  box-shadow: 0 2px 12px rgba(0,0,0,0.1);
  width: 300px;
  height: 200px;
  background: #fff;
}

2.4 运算(+ - * /)—— 自动计算,减少手动计算

支持颜色、数值、单位运算,Less自动处理单位(优先左侧单位),避免手动计算错误,代码更优雅。


@base: 20px;
@width: 1000px;
@card-width: @width / 5; // 200px,自动计算栅格宽度
@light-primary: lighten(@primary, 10%); // 主色变亮10%,无需手动计算色值

.box {
  width: @width - 40px; // 960px,自适应宽度
  padding: @base * 1.5; // 30px,间距按比例调整
  margin: (@base / 2); // 10px,统一间距
}
.card {
  width: @card-width;
  background: @light-primary;
  margin: @base;
}

⚠️ 除法建议加括号:(100px / 2),避免与CSS语法冲突

编译后CSS(对比:自动计算数值和颜色,无需手动计算):


.box {
  width: 960px;
  padding: 30px;
  margin: 10px;
}
.card {
  width: 200px;
  background: #3b82f6; // 自动计算的亮主色
  margin: 20px;
}

2.5 内置函数—— 颜色、字符串、数学处理(常用)

Less提供大量内置函数,无需定义直接用,提升效率,让样式更优雅。


// 颜色函数(最常用,自动处理色值)
@primary: #2563eb;
@dark-primary: darken(@primary, 10%); // 主色变暗10%
@fade-primary: fade(@primary, 50%); // 主色半透明
@saturate-primary: saturate(@primary, 20%); // 主色增加饱和度

// 数学函数(自动处理数值)
@base-font: 16px;
@title-font: ceil(@base-font * 1.5); // 24px,向上取整
@sub-font: floor(@base-font * 0.8); // 12px,向下取整

// 使用:结合函数和变量,样式更灵活
.title {
  font-size: @title-font;
  color: @dark-primary;
}
.sub-title {
  font-size: @sub-font;
  color: @fade-primary;
}
.btn {
  background: @saturate-primary;
  &:hover {
    background: @dark-primary;
  }
}

编译后CSS:


.title {
  font-size: 24px;
  color: #1d4ed8; // 变暗后的主色
}
.sub-title {
  font-size: 12px;
  color: rgba(37, 99, 235, 0.5); // 半透明主色
}
.btn {
  background: #1d4ed8; // 增加饱和度后的主色
}
.btn:hover {
  background: #1d4ed8;
}

2.6 导入(@import)—— 模块化拆分,代码解耦

拆分变量、Mixin、公共样式,通过@import合并,支持省略.less后缀,让代码结构更清晰,便于维护。


// 1. 拆分文件(按功能拆分,各司其职)
// variables.less → 全局变量(单独管理,一改全改)
@primary: #2563eb;
@font-size: 16px;
@spacing: 20px;

// mixins.less → 公共混合(单独管理,全局复用)
.clearfix() { ... }
.btn(@bg: @primary) { ... }
.center() { ... }

// base.less → 基础样式(reset、全局样式)
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}
body {
  font-family: "Microsoft YaHei", sans-serif;
  font-size: @font-size;
  color: #333;
}

// 2. 主文件导入(统一入口,结构清晰)
@import "variables"; // 省略.less
@import "mixins";
@import "base";

// 编写业务样式(仅关注业务,无需关注公共样式)
.header {
  .clearfix();
  height: 60px;
  .nav {
    .center();
    a {
      color: #333;
      &:hover { color: @primary; }
    }
  }
}

⚠️ @import (reference) "mixins.less"; → 仅导入Mixin,不输出到CSS

编译后CSS(对比:所有导入的样式自动合并,结构清晰):


* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}
body {
  font-family: "Microsoft YaHei", sans-serif;
  font-size: 16px;
  color: #333;
}
.header {
  height: 60px;
}
.header::after {
  content: "";
  display: block;
  clear: both;
  height: 0;
  visibility: hidden;
}
.header .nav {
  display: flex;
  justify-content: center;
  align-items: center;
}
.header .nav a {
  color: #333;
}
.header .nav a:hover {
  color: #2563eb;
}

2.7 作用域与注释

  • 作用域:就近原则,局部变量覆盖全局变量,类似JS,可灵活控制变量作用范围

// 全局变量(整个项目可用)
@color: red;

// 局部变量(仅在.box内可用,不影响全局)
.box {
  @color: blue;
  color: @color; // blue(局部优先)
}
// 其他模块仍使用全局变量
.text {
  color: @color; // red
}

编译后CSS:


.box {
  color: blue;
}
.text {
  color: red;
}
  • 注释:

    • 单行注释:// 注释 → 编译后不保留(用于开发备注,不污染生产CSS)
    • 多行注释:/* 注释 */ → 编译后保留(用于生产环境备注,如版权信息)

三、Less进阶技巧(提升效率,避坑)

3.1 父选择器&高级用法


// 1. 前缀拼接(批量生成同类样式,避免重复)
.btn {
  display: inline-block;
  padding: 8px 16px;
  border-radius: 4px;
  &-primary { background: @primary; color: #fff; } // .btn-primary
  &-success { background: @success; color: #fff; } // .btn-success
  &-warning { background: #f59e0b; color: #fff; } // .btn-warning
  &-disabled { background: #ccc; color: #666; cursor: not-allowed; } // .btn-disabled
}

// 2. 多层嵌套&(精准定位子元素,避免冗长选择器)
.list {
  width: 100%;
  &-item {
    padding: @spacing;
    border-bottom: 1px solid #eee;
    &:last-child { border-bottom: none; } // .list-item:last-child
    &-title { font-weight: bold; color: #333; } // .list-item-title
    &-content { color: #666; margin-top: 8px; } // .list-item-content
  }
}

编译后CSS(对比:自动拼接选择器,批量生成样式,代码简洁):


.btn {
  display: inline-block;
  padding: 8px 16px;
  border-radius: 4px;
}
.btn-primary {
  background: #2563eb;
  color: #fff;
}
.btn-success {
  background: #16a34a;
  color: #fff;
}
.btn-warning {
  background: #f59e0b;
  color: #fff;
}
.btn-disabled {
  background: #ccc;
  color: #666;
  cursor: not-allowed;
}

.list {
  width: 100%;
}
.list-item {
  padding: 20px;
  border-bottom: 1px solid #eee;
}
.list-item:last-child {
  border-bottom: none;
}
.list-item-title {
  font-weight: bold;
  color: #333;
}
.list-item-content {
  color: #666;
  margin-top: 8px;
}

3.2 条件Mixin(when)—— 动态生成样式


// 定义:根据尺寸生成不同按钮(条件判断,灵活适配)
.btn(@size) when (@size = large) {
  padding: 12px 24px;
  font-size: 18px;
  border-radius: 6px;
}
.btn(@size) when (@size = small) {
  padding: 4px 8px;
  font-size: 14px;
  border-radius: 3px;
}
// 新增条件:根据主题生成不同颜色
.btn(@size, @theme) when (@theme = dark) {
  .btn(@size);
  background: #333;
  color: #fff;
}

// 使用:按需传入条件,自动生成对应样式
.btn-lg { .btn(large); } // 大尺寸按钮
.btn-sm { .btn(small); } // 小尺寸按钮
.btn-lg-dark { .btn(large, dark); } // 大尺寸深色按钮

编译后CSS:


.btn-lg {
  padding: 12px 24px;
  font-size: 18px;
  border-radius: 6px;
}
.btn-sm {
  padding: 4px 8px;
  font-size: 14px;
  border-radius: 3px;
}
.btn-lg-dark {
  padding: 12px 24px;
  font-size: 18px;
  border-radius: 6px;
  background: #333;
  color: #fff;
}

3.3 循环(for)—— 批量生成样式(Less 3.9+)


// 生成1-5列栅格(循环遍历,无需手动写5个样式)
.generate-columns(@n, @i: 1) when (@i =< @n) {
  .col-@{i} {
    width: (@i * 100% / @n);
    float: left;
    padding: @spacing/2;
    box-sizing: border-box;
  }
  .generate-columns(@n, @i + 1); // 递归循环
}
.generate-columns(5); // 生成col-1到col-5

// 生成不同尺寸的margin样式(批量生成,复用性强)
.generate-margin(@n, @i: 1) when (@i =< @n) {
  .mt-@{i} { margin-top: @i * 8px; }
  .mb-@{i} { margin-bottom: @i * 8px; }
  .generate-margin(@n, @i + 1);
}
.generate-margin(5); // 生成mt-1~mt-5、mb-1~mb-5

编译后CSS(对比:自动生成10个margin样式+5个栅格样式,无需手动书写):


.col-1 {
  width: 20%;
  float: left;
  padding: 10px;
  box-sizing: border-box;
}
.col-2 {
  width: 40%;
  float: left;
  padding: 10px;
  box-sizing: border-box;
}
.col-3 {
  width: 60%;
  float: left;
  padding: 10px;
  box-sizing: border-box;
}
.col-4 {
  width: 80%;
  float: left;
  padding: 10px;
  box-sizing: border-box;
}
.col-5 {
  width: 100%;
  float: left;
  padding: 10px;
  box-sizing: border-box;
}

.mt-1 { margin-top: 8px; }
.mb-1 { margin-bottom: 8px; }
.mt-2 { margin-top: 16px; }
.mb-2 { margin-bottom: 16px; }
.mt-3 { margin-top: 24px; }
.mb-3 { margin-bottom: 24px; }
.mt-4 { margin-top: 32px; }
.mb-4 { margin-bottom: 32px; }
.mt-5 { margin-top: 40px; }
.mb-5 { margin-bottom: 40px; }

3.4 映射(Maps)—— 像对象一样取值(Less 3.5+)


// 定义颜色映射(类似JS对象,统一管理所有颜色,便于查找和修改)
@colors: {
  primary: #2563eb;
  success: #16a34a;
  warning: #f59e0b;
  danger: #ef4444;
  dark: #333;
  light: #f5f5f5;
};

// 定义尺寸映射(统一管理尺寸,避免硬编码)
@sizes: {
  small: 14px;
  base: 16px;
  large: 18px;
  xlarge: 24px;
};

// 使用:通过映射取值,代码更简洁,维护更方便
.btn {
  font-size: @sizes[base];
  &-primary { background: @colors[primary]; }
  &-success { background: @colors[success]; }
  &-warning { background: @colors[warning]; }
}
.title {
  font-size: @sizes[xlarge];
  color: @colors[dark];
}

编译后CSS:


.btn {
  font-size: 16px;
}
.btn-primary {
  background: #2563eb;
}
.btn-success {
  background: #16a34a;
}
.btn-warning {
  background: #f59e0b;
}
.title {
  font-size: 24px;
  color: #333;
}

四、实战规范与常见问题(避坑指南)

4.1 项目规范(推荐)

  1. 文件结构:

src/
├── less/
│   ├── variables.less   # 全局变量(颜色、尺寸、字体)
│   ├── mixins.less      # 公共混合(清除浮动、按钮、居中)
│   ├── base.less        # 基础样式(reset、全局)
│   ├── components/      # 组件(按钮、卡片、导航)
│   └── main.less        # 主入口(导入所有)
  1. 命名:变量用@xxx-xxx(@primary-color),Mixin用小驼峰/短横线,语义化

4.2 常见问题与解决

  1. 嵌套过深:最多3层,避免编译后选择器过长、性能差
  2. 变量污染:全局变量放单独文件,局部变量仅在模块内使用
  3. 编译报错:检查括号、分号、变量定义,优先用Easy LESS实时提示
  4. 单位冲突:运算时统一单位,或用unit()函数转换

五、Less vs Sass(快速对比,选择更合适)

特性 Less Sass
变量符号 @ $
编译环境 Node.js/浏览器 Ruby/Node.js
语法 接近CSS,易上手 缩进/花括号两种
循环/条件 支持(Less 3.9+) 原生支持,更强大
生态 轻量,适合中小型项目 功能全,适合大型项目

六、总结与下一步

Less核心就是把CSS变成可维护的代码:变量统一、嵌套清晰、Mixin复用、函数简化、模块化拆分。新增的优雅案例均贴合实际开发,通过Less源码与编译后CSS对比,可清晰看到Less如何简化编码、提升效率。

CSS3 position 属性全面理解(实战版)

CSS3 的 position 属性,核心作用是控制元素在页面中的定位方式,决定元素如何脱离正常文档流、如何相对于父元素/视口进行定位,是布局中实现“悬浮、固定、层叠”等效果的核心属性,常用且易混淆,本篇将从基础到实战,彻底讲懂。

核心原则:position 的取值决定元素的定位模式,配合 toprightbottomleft(以下简称“方位属性”)控制具体位置,未设置方位属性时,元素默认保持原有位置。

一、position 5个取值(必学,按常用度排序)

position 有5个核心取值,其中 static 是默认值,relativeabsolutefixed 是高频常用值,sticky 是粘性定位(场景化常用),逐一拆解如下。

1. static(默认值:静态定位)

最基础的定位方式,元素遵循正常文档流,自上而下、从左到右排列,不受方位属性(top/right等)的影响,也不会脱离文档流。

/* 默认无需主动设置,所有元素默认都是 static */
.box {
  position: static;
  top: 20px; /* 无效,static 不识别方位属性 */
  left: 20px; /* 无效 */
  width: 100px;
  height: 100px;
  background: #00aaff;
}

特点:无法通过方位属性调整位置,完全遵循正常布局,一般用于“取消已设置的定位”(如覆盖父元素的定位)。

2. relative(相对定位)

元素不脱离正常文档流,保留自身在文档流中的原有位置,方位属性(top/right等)相对于自身原有位置进行偏移,不会影响其他元素的布局。

.box {
  position: relative;
  top: 20px; /* 相对于自身原有位置,向下偏移20px */
  left: 30px; /* 相对于自身原有位置,向右偏移30px */
  width: 100px;
  height: 100px;
  background: #ff7d00;
}

关键要点:

  • 不脱离文档流,自身原有位置会被保留(其他元素不会填充进来);
  • 偏移量相对于“自身原本的位置”(而非父元素);
  • 常用场景:作为 absolute 定位的“参考容器”(子绝父相),或微调元素位置。

3. absolute(绝对定位)【重点】

元素完全脱离正常文档流,不再保留自身原有位置(其他元素会填充其空位),方位属性相对于最近的已定位祖先元素(position 不为 static 的祖先)定位;若没有已定位祖先,则相对于根元素(html)定位。

/* 父容器设置 relative(作为参考) */
.parent {
  position: relative;
  width: 300px;
  height: 300px;
  background: #f5f5f5;
}
/* 子元素设置 absolute(相对于父容器定位) */
.child {
  position: absolute;
  top: 50px; /* 相对于父容器顶部,向下50px */
  right: 50px; /* 相对于父容器右侧,向左50px */
  width: 100px;
  height: 100px;
  background: #00cc66;
}

关键要点(避坑核心):

  • 脱离文档流,不占据页面空间,会“悬浮”在其他元素上方;
  • 定位参考物:优先找“最近的、position 为 relative/absolute/fixed/sticky”的祖先元素,没有则相对于 html(页面);
  • 高频场景:弹窗、下拉菜单、元素悬浮、精准定位(如按钮右上角的角标);
  • 易错点:若父元素未设置定位,子元素 absolute 会相对于页面定位,导致布局错乱。

4. fixed(固定定位)

元素完全脱离正常文档流,方位属性相对于浏览器视口(viewport) 定位,无论页面如何滚动,元素始终固定在视口的指定位置,不随页面滚动而移动。

/* 页面右下角固定按钮 */
.fixed-btn {
  position: fixed;
  bottom: 30px; /* 相对于视口底部,向上30px */
  right: 30px; /* 相对于视口右侧,向左30px */
  width: 60px;
  height: 60px;
  border-radius: 50%;
  background: #ff4444;
  color: white;
  text-align: center;
  line-height: 60px;
}

关键要点:

  • 脱离文档流,不占据页面空间,始终悬浮在视口固定位置;
  • 定位参考物是“浏览器视口”,与父元素无关;
  • 常用场景:固定导航栏、回到顶部按钮、悬浮客服按钮。

5. sticky(粘性定位)

“相对定位 + 固定定位”的结合体,元素默认遵循正常文档流,当页面滚动到指定位置时,自动切换为固定定位,固定在视口的指定位置;滚动超出范围后,恢复为相对定位。

/* 粘性导航栏 */
.sticky-nav {
  position: sticky;
  top: 0; /* 滚动到顶部距离为0时,固定 */
  width: 100%;
  height: 60px;
  background: #333;
  color: white;
  line-height: 60px;
  padding: 0 20px;
}

关键要点:

  • 未滚动到指定位置时,遵循正常文档流;滚动到阈值(top/right等设置的值)时,变为固定定位;
  • 必须设置方位属性(top/right/bottom/left),否则粘性效果无效;
  • 常用场景:粘性导航栏、列表标题悬浮(滚动时标题固定在顶部)。

二、核心对比(快速区分,避坑关键)

取值 是否脱离文档流 定位参考物 核心场景
static 无(不识别方位属性) 默认布局、取消定位
relative 自身原有位置 作为absolute参考容器、微调位置
absolute 最近的已定位祖先 / html 弹窗、悬浮元素、精准定位
fixed 浏览器视口 固定导航、回到顶部按钮
sticky 视滚动状态(未固定时否,固定时是) 正常流时自身,固定时视口 粘性导航、悬浮标题

三、实战案例(直接复制套用,覆盖高频场景)

案例1:子绝父相(最常用,精准定位)

场景:卡片内部的按钮、角标,相对于卡片精准定位,不影响卡片布局。

/* 父卡片 - 相对定位(参考容器) */
.card {
  position: relative;
  width: 280px;
  height: 380px;
  background: white;
  border-radius: 8px;
  box-shadow: 0 2px 12px rgba(0,0,0,0.1);
  padding: 20px;
}
/* 子元素 - 绝对定位(相对于卡片) */
.card-badge {
  position: absolute;
  top: 10px;
  right: 10px;
  background: #ff4444;
  color: white;
  padding: 2px 8px;
  border-radius: 12px;
  font-size: 12px;
}
.card-btn {
  position: absolute;
  bottom: 20px;
  left: 50%;
  transform: translateX(-50%); /* 水平居中 */
  width: 80%;
  height: 40px;
  background: #00aaff;
  color: white;
  border: none;
  border-radius: 4px;
}

案例2:固定回到顶部按钮

场景:页面滚动时,右下角固定显示回到顶部按钮,点击可返回页面顶部。

.back-top {
  position: fixed;
  bottom: 30px;
  right: 30px;
  width: 50px;
  height: 50px;
  border-radius: 50%;
  background: rgba(0, 170, 255, 0.8);
  color: white;
  text-align: center;
  line-height: 50px;
  cursor: pointer;
  transition: all 0.3s;
}
.back-top:hover {
  background: #00aaff;
  transform: scale(1.1);
}

案例3:粘性导航栏

场景:页面顶部导航栏,滚动页面时,导航栏固定在视口顶部,不随页面滚动消失。

/* 页面头部 */
.header {
  height: 100px;
  background: #f5f5f5;
  line-height: 100px;
  text-align: center;
  font-size: 24px;
}
/* 粘性导航 */
.sticky-nav {
  position: sticky;
  top: 0;
  width: 100%;
  height: 60px;
  background: #333;
  color: white;
  padding: 0 20px;
  box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
/* 导航菜单 */
.nav-menu {
  display: flex;
  gap: 30px;
  line-height: 60px;
}
/* 页面内容(用于测试滚动) */
.content {
  height: 1500px;
  padding: 20px;
}

四、避坑注意事项(必看)

  • absolute 定位的元素,若父元素未设置定位(position 为 static),会相对于 html 定位,容易导致布局错乱,建议遵循“子绝父相”原则。
  • fixed 定位的元素,会脱离文档流且相对于视口定位,若父元素有 transform 属性(如 scale、translate),会导致 fixed 定位失效(参考物变为父元素)。
  • sticky 定位必须设置方位属性(top/right等),否则无法触发粘性效果;且父元素不能有 overflow: hidden 属性,否则粘性效果失效。
  • 脱离文档流的元素(absolute、fixed),会覆盖在未脱离文档流的元素上方,可通过 z-index 属性调整层级(z-index 值越大,层级越高)。
  • relative 定位的元素,虽然不脱离文档流,但会创建“层叠上下文”,其内部的绝对定位元素会相对于它定位,而非相对于更外层的元素。

五、总结(快速记忆)

  1. 默认 static:不脱离流,方位无效;

  2. relative:不脱离流,相对于自身,做参考容器;

  3. absolute:脱离流,相对于已定位祖先,精准定位;

  4. fixed:脱离流,相对于视口,固定不动;

  5. sticky:混合定位,滚动触发固定,需设方位属性。

记住“子绝父相”“fixed 相对视口”“sticky 需设方位”这3个关键点,就能解决90%的定位场景,剩下的靠实战熟练即可。

零基础学会 Flex 布局(实战版)

Flex 布局(弹性布局)是 CSS 中最常用、最灵活的布局方式,核心作用是快速实现元素的对齐、分布、自适应,替代传统的 float、position 布局,适配所有现代浏览器,上手简单且实用性极强。

核心原则:给父容器设置 display: flex,父容器称为「flex 容器」,其直接子元素称为「flex 项目」,通过控制容器和项目的属性,实现各种布局效果。

一、核心基础(必学,3分钟掌握)

1. 开启 Flex 布局(第一步)

给需要布局的父容器添加 display: flex,即可开启弹性布局,项目会默认横向排列(水平主轴)。

/* 父容器 */
.flex-container {
  display: flex; /* 开启flex布局 */
  width: 100%;
  height: 300px;
  background: #f5f5f5;
}

/* 子项目 */
.flex-item {
  width: 100px;
  height: 100px;
  background: #00aaff;
  margin: 10px;
}

效果:3个项目横向排列,默认在容器顶部对齐,自动填满水平方向(未填充满时,项目之间有间隙)。

2. 两个核心概念(必记)

  • 主轴:项目排列的主要方向(默认「水平方向」,从左到右)
  • 交叉轴:与主轴垂直的方向(默认「垂直方向」,从上到下)

后续所有属性,都是围绕「主轴」和「交叉轴」的对齐、分布来设置。

二、容器属性(控制整体布局,最常用6个)

所有属性均设置在「父容器」上,控制子项目的排列方式,重点记前4个。

1. flex-direction(控制主轴方向,核心)

决定项目是横向、纵向排列,解决“水平/垂直布局”问题。

属性值 效果 常用场景
row(默认) 主轴水平,项目从左到右 导航栏、横向卡片
column 主轴垂直,项目从上到下 侧边栏、纵向列表
row-reverse 主轴水平,项目从右到左 反向排列(少见)
column-reverse 主轴垂直,项目从下到上 反向排列(少见)

2. justify-content(主轴对齐,最常用)

控制项目在「主轴」上的对齐方式,解决“水平/垂直居中、分布”问题。

属性值 效果(主轴水平时) 高频场景
flex-start(默认) 项目靠左对齐 普通列表
center 项目水平居中 登录框、卡片居中
flex-end 项目靠右对齐 右侧按钮组
space-between 两端对齐,项目之间间距相等 导航栏(左右分布)
space-around 项目两侧间距相等,整体间距均匀 卡片布局、商品列表

3. align-items(交叉轴对齐,核心)

控制项目在「交叉轴」上的对齐方式,解决“垂直/水平居中”的另一半问题。

属性值 效果(主轴水平时) 高频场景
stretch(默认) 项目高度拉伸至与容器一致 等高卡片、导航栏
center 项目垂直居中 文字+图标对齐、卡片内容居中
flex-start 项目靠上对齐 顶部列表
flex-end 项目靠下对齐 底部按钮组

4. flex-wrap(控制换行,避免溢出)

默认情况下,项目会强制在一行显示,超出容器会溢出,用这个属性控制换行。

属性值 效果
nowrap(默认) 不换行,项目会被压缩
wrap 自动换行,超出一行时,新行在下
wrap-reverse 自动换行,超出一行时,新行在上

5. align-content(多行交叉轴对齐,少见)

只有当项目换行(flex-wrap: wrap)时才生效,控制多行项目在交叉轴上的整体对齐方式,用法和 justify-content 类似(center、space-between 等)。

6. flex-flow(简写属性,偷懒必备)

flex-direction + flex-wrap 的简写,顺序任意,默认值:flex-flow: row nowrap。

/* 等价于 flex-direction: column; flex-wrap: wrap; */
.flex-container {
  flex-flow: column wrap;
}

三、项目属性(控制单个项目,常用3个)

属性设置在「子项目」上,控制单个项目的大小、对齐方式,按需使用。

1. flex(核心,控制项目占比)

最常用属性,控制项目在主轴上的占比,替代 width/height,实现自适应,语法:flex: 数字(数字越大,占比越大)。

.flex-container {
  display: flex;
  width: 100%;
  height: 200px;
  background: #f5f5f5;
}
/* 三个项目,占比 1:2:1 */
.item1 { flex: 1; background: #00aaff; }
.item2 { flex: 2; background: #ff7d00; }
.item3 { flex: 1; background: #00cc66; }

效果:容器宽度被分成 4 份,item2 占 2 份,其余各占 1 份,自适应容器宽度。

补充:flex 的完整写法是 flex: flex-grow flex-shrink flex-basis,日常用简写(数字)即可。

2. align-self(单个项目交叉轴对齐)

覆盖父容器的 align-items 属性,单独控制某个项目的交叉轴对齐方式。

.flex-container {
  display: flex;
  align-items: center; /* 所有项目垂直居中 */
}
.item-special {
  align-self: flex-end; /* 单个项目靠下对齐 */
}

补充:flex 的完整写法是 flex: flex-grow flex-shrink flex-basis,日常用简写(数字)即可。

3. order(控制项目顺序)

默认所有项目 order: 0,数字越小,项目越靠前;可以用负数,实现“不改变HTML结构,调整显示顺序”。

.item1 { order: 2; } /* 第3个显示 */
.item2 { order: 1; } /* 第2个显示 */
.item3 { order: 0; } /* 第1个显示 */

四、实战案例(直接复制套用,覆盖80%场景)

案例1:水平居中 + 垂直居中(最常用)

适用于登录框、弹窗内容、卡片居中,一步到位。

.container {
  display: flex;
  justify-content: center; /* 主轴居中(水平) */
  align-items: center; /* 交叉轴居中(垂直) */
  width: 100vw; /* 视口宽度 */
  height: 100vh; /* 视口高度 */
  background: #f5f5f5;
}
.center-box {
  width: 300px;
  height: 200px;
  background: white;
  border-radius: 8px;
  text-align: center;
  line-height: 200px;
}

案例2:导航栏(两端对齐)

适用于网站导航,左侧logo、右侧菜单,两端分布。

.nav {
  display: flex;
  justify-content: space-between; /* 两端对齐 */
  align-items: center; /* 垂直居中 */
  padding: 0 20px;
  height: 60px;
  background: #333;
  color: white;
}
.nav-logo {
  font-size: 20px;
  font-weight: bold;
}
.nav-menu {
  display: flex; /* 菜单横向排列 */
  gap: 20px; /* 菜单之间间距 */
}

案例3:自适应卡片布局(换行)

适用于商品列表、卡片展示,自适应屏幕宽度,自动换行。

.card-container {
  display: flex;
  flex-wrap: wrap; /* 自动换行 */
  gap: 20px; /* 卡片之间间距 */
  padding: 20px;
  background: #f5f5f5;
}
.card {
  flex: 1; /* 自适应占比 */
  min-width: 250px; /* 最小宽度,避免太窄 */
  height: 200px;
  background: white;
  border-radius: 8px;
  padding: 15px;
}

案例4:垂直布局(侧边栏)

适用于侧边导航、垂直列表,纵向排列。

.sidebar {
  display: flex;
  flex-direction: column; /* 垂直排列 */
  width: 200px;
  height: 500px;
  background: #333;
  color: white;
}
.sidebar-item {
  padding: 15px 20px;
  border-bottom: 1px solid #444;
}
.sidebar-item:last-child {
  border-bottom: none;
}

五、注意事项(避坑关键)

  • 开启 flex 布局后,子项目的 float、clear、vertical-align 属性会失效,无需再用。
  • flex: 1 等价于 flex: 1 1 0%,会让项目自适应填充剩余空间,优先于固定 width/height。
  • justify-content 控制主轴,align-items 控制交叉轴,方向由 flex-direction 决定(别搞反)。
  • 项目换行时,用 gap 控制间距(比 margin 更简洁,不会出现最后一个项目多余间距)。
  • 兼容问题:Flex 布局支持 IE10+,现代浏览器(Chrome、Edge、Firefox、Safari)均完美支持,无需加前缀。

六、总结(快速记忆)

  1. 开启:父容器 display: flex

  2. 方向:flex-direction(row/column);

  3. 对齐:主轴 justify-content,交叉轴 align-items;

  4. 换行:flex-wrap: wrap;

  5. 占比:项目 flex: 数字;

记住这5点,就能解决80%的布局问题,剩下的靠实战熟练即可。

Vue 中实现文字滚动(跑马灯)的多种方式

在 Vue 里实现文字滚动(跑马灯) ,最常用、最稳的就两种:

  1. CSS 动画纯实现(简单、性能好)
  2. JS 控制滚动(可暂停、可控制速度)

下面直接给你可复制粘贴的 Vue 组件代码


方式1:纯 CSS 跑马灯(推荐)

Marquee.vue

<template>
  <div class="marquee-wrap">
    <div class="marquee-content">
      {{ text }}
    </div>
  </div>
</template>

<script setup>
const text = '这里是需要滚动的文字,Vue 跑马灯效果,从右向左无限滚动~';
</script>

<style scoped>
.marquee-wrap {
  width: 100%;
  overflow: hidden;
  white-space: nowrap;
  background: #f5f5f5;
  padding: 8px 16px;
  border-radius: 8px;
}

.marquee-content {
  display: inline-block;
  animation: marquee 15s linear infinite;
}

@keyframes marquee {
  0% {
    transform: translateX(100%);
  }
  100% {
    transform: translateX(-100%);
  }
}
</style>

特点:

  • 一行无限滚动
  • 无 JS,性能最好
  • 鼠标悬浮暂停版往下看

方式2:hover 暂停 + 无缝滚动(更常用)

<template>
  <div class="box">
    <div class="marquee" @mouseenter="pause" @mouseleave="play">
      <div class="text" :style="{ animationPlayState }">
        {{ content }}
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref } from 'vue';

const content = 'Vue3 无缝跑马灯,鼠标移入暂停,移出继续滚动~';
const animationPlayState = ref('running');

const pause = () => {
  animationPlayState.value = 'paused';
};
const play = () => {
  animationPlayState.value = 'running';
};
</script>

<style scoped>
.box {
  width: 100%;
  overflow: hidden;
  background: #f9f9f9;
  padding: 10px;
  border-radius: 6px;
}
.marquee {
  white-space: nowrap;
}
.text {
  display: inline-block;
  animation: move 12s linear infinite;
}
@keyframes move {
  0% { transform: translateX(100%); }
  100% { transform: translateX(-100%); }
}
</style>

方式3:真正无缝(无空白,首尾衔接)

适合公告、长文本:

<template>
  <div class="wrap">
    <div class="box">
      <span class="txt1">{{ text }}</span>
      <span class="txt2">{{ text }}</span>
    </div>
  </div>
</template>

<script setup>
const text = '这里是真正无缝跑马灯,没有空白间隔,一直循环滚动';
</script>

<style scoped>
.wrap {
  width: 100%;
  overflow: hidden;
  background: #fff8e1;
  padding: 8px 0;
}
.box {
  display: flex;
  width: max-content;
  animation: scroll 10s linear infinite;
}
.txt1, .txt2 {
  padding: 0 20px;
}
@keyframes scroll {
  0% { transform: translateX(0); }
  100% { transform: translateX(-50%); }
}
</style>

方式4:JS 控制滚动(可变速、可停止)

<template>
  <div class="box" style="overflow: hidden">
    <div class="text" :style="{ marginLeft: `${left}px` }">
      JS 控制跑马灯,可随时停止、加速、减速
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
const left = ref(300);
let timer = null;

onMounted(() => {
  timer = setInterval(() => {
    left.value -= 1;
    if (left.value < -300) left.value = 300;
  }, 20);
});
onUnmounted(() => clearInterval(timer));
</script>

CSS3 阴影完全指南:box-shadow、text-shadow 详解

shadow 里最常用的两个阴影属性:

  • text-shadow:文字阴影(简单)
  • box-shadow:盒子/元素阴影(常用、功能强)

我用最通俗、能直接上手的方式给你讲清楚,附带复制即用的代码。


一、text-shadow 文字阴影

专门给文字加阴影,语法超级简单。

基础语法

text-shadow: 水平偏移 垂直偏移 模糊度 颜色;

参数解释

  1. 水平偏移:正数向右,负数向左
  2. 垂直偏移:正数向下,负数向上
  3. 模糊度:值越大越模糊(不能为负)
  4. 颜色:阴影颜色(可省略,默认文字颜色)

示例代码

/* 基础阴影 */
text-shadow: 2px 2px 4px #333;

/* 发光效果(最常用!) */
text-shadow: 0 0 8px #00aaff;

/* 多重阴影(用逗号分隔) */
text-shadow: 1px 1px 2px black, 0 0 1em blue, 0 0 0.2em blue;

二、box-shadow 盒子阴影(重点!)

div、图片、按钮、卡片加阴影,90% 网页美化都用它。

完整语法

box-shadow: 水平偏移 垂直偏移 模糊度 扩散半径 颜色 内外阴影;

6 个参数(必须记)

  1. h-shadow:水平偏移(必需)
  2. v-shadow:垂直偏移(必需)
  3. blur:模糊半径(越大越柔)
  4. spread:扩散半径(扩大/缩小阴影)
  5. color:阴影颜色
  6. inset内阴影(不加就是外阴影)

最常用写法(外阴影)

/* 卡片柔和阴影(推荐!) */
box-shadow: 0 2px 12px rgba(0,0,0,0.1);

内阴影(inset)

/* 凹陷效果 */
box-shadow: inset 0 0 10px #000;

多重阴影

box-shadow: 
  0 0 10px red,
  0 0 20px blue,
  0 0 30px green;

三、最实用的 6 种阴影效果(直接复制)

1. 标准卡片阴影(最常用)

box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);

2. 悬浮抬起效果(hover)

div{
  transition: 0.3s;
}
div:hover{
  box-shadow: 0 8px 24px rgba(0,0,0,0.15);
  transform: translateY(-3px);
}

3. 发光效果

box-shadow: 0 0 12px #00aaff;

4. 内阴影(凹陷)

box-shadow: inset 0 2px 8px rgba(0,0,0,0.2);

5. 细边框阴影(代替 border)

box-shadow: 0 0 0 1px #ccc;

6. 文字发光

text-shadow: 0 0 6px #fff, 0 0 12px #00aaff;

四、快速区分 & 记忆

属性 作用 最常用格式
text-shadow 文字阴影 2px 2px 4px #333
box-shadow 盒子阴影 0 2px 12px rgba(0,0,0,0.1)
  • 偏移:正右下,负左上
  • 模糊:越大越柔
  • 扩散:越大越大
  • 内阴影:加 inset

五、小技巧(高手必备)

  1. 阴影用 rgba 更自然:rgba(0,0,0,0.1)
  2. 垂直偏移 > 水平偏移更符合视觉习惯
  3. 配合 transition 做 hover 动画超好看
  4. 多重阴影 = 高级质感

总结

  • text-shadow:给文字加阴影,4个参数,简单
  • box-shadow:给盒子加阴影,6个参数,支持内外、多层
  • 最实用:0 2px 12px rgba(0,0,0,0.1) 卡片阴影
  • 想凹陷就加 inset

需要我给你做一个可在线调试的阴影演示页面吗?直接复制就能用!

❌