Skip to content

Less 常用语法

大白话解释: Less 和 Sass 一样,都是"增强版 CSS"。给 CSS 加了变量、嵌套、混合等功能,让样式代码更易维护。

Less vs Sass 怎么选?

  • Less:学习曲线低,语法更接近 CSS,老项目常见
  • Sass:功能更强大,社区生态更好,新项目推荐

什么时候用 Less?

  • 老项目维护(历史技术栈使用 Less)
  • 团队成员不熟悉 Sass
  • 项目不太复杂,只需要变量和嵌套

Less 是 CSS 预处理器,扩展了 CSS 语言,增加了变量、混合、函数等功能。

安装

bash
# 全局安装(用于命令行编译)
npm install -g less

# 项目安装(推荐)
npm install -D less

# 编译单个文件
lessc styles.less styles.css

# 压缩编译(需先安装 less-plugin-clean-css)
npm install -g less-plugin-clean-css
lessc --clean-css styles.less styles.min.css

# 监听变化(需安装 less-watch-compiler)
npm install -g less-watch-compiler
less-watch-compiler src dist style.less

变量

基本用法

less
// 定义变量:用 @ 符号声明,编译时会被替换为实际值
@primary: #1890ff;        // 主色:按钮、链接等核心颜色
@success: #52c41a;        // 成功色:成功状态提示
@danger: #ff4d4f;         // 危险色:错误、删除操作
@warning: #faad14;        // 警告色:警告状态提示

// 间距:统一管理组件内外边距,方便全局调整
@spacing-xs: 4px;
@spacing-sm: 8px;
@spacing-md: 16px;
@spacing-lg: 24px;
@spacing-xl: 32px;

// 字体:集中定义字号,保持排版一致性
@font-size-sm: 12px;
@font-size-base: 14px;
@font-size-lg: 16px;
@font-size-xl: 20px;
@font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto;

// 圆角:按钮、卡片等组件的圆角统一管理
@border-radius-sm: 2px;
@border-radius-base: 4px;
@border-radius-lg: 8px;
@border-radius-round: 50%;

// 阴影:不同层级的投影效果
@box-shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.09);
@box-shadow-base: 0 4px 16px rgba(0, 0, 0, 0.12);
@box-shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.15);

// z-index 层级:统一管理层叠顺序,避免 z-index 混乱
@z-index-dropdown: 1000;
@z-index-modal: 2000;
@z-index-tooltip: 3000;

// 使用变量:直接用 @变量名 引用
.button {
  background: @primary;
  padding: @spacing-sm @spacing-md;
  font-size: @font-size-base;
  border-radius: @border-radius-base;
  box-shadow: @box-shadow-sm;
}

变量计算

less
@base: 16px;
@double: @base * 2;         // 32px
@half: (@base / 2);         // 8px(Less 4+ 中 / 需要括号才计算)
@percentage: (100% / 3);    // 33.33333%

.container {
  width: 100% - 20%;        // 80%
  padding: @half;
  font-size: @base + 2px;   // 18px
}
css
/* 编译后:变量计算在编译期完成。注意:Less 4+ 中 / 需要括号才作为除法运算 */
.container {
  width: 80%;
  padding: 8px;
  font-size: 18px;
}

变量作用域

less
@color: red;

.header {
  @color: blue;           // 局部变量
  color: @color;          // blue
}

.footer {
  color: @color;          // red(使用全局变量)
}

// 延迟加载(Less 特性)
.lazy {
  color: @var;
}

@var: red;                // 变量定义在使用之后也可以
css
/* 编译后:变量被替换为实际值,局部变量只在对应选择器内生效 */
.header {
  color: blue;
}

.footer {
  color: red;
}

.lazy {
  color: red;  /* 延迟加载:变量定义在使用之后也能正常解析 */
}

嵌套

基本嵌套

less
.nav {
  background: #fff;         // 导航栏背景色
  height: 60px;             // 导航栏高度
  
  .nav-item {
    display: inline-block;   // 水平排列
    padding: 0 16px;         // 左右内边距
    line-height: 60px;       // 行高 = 容器高度,实现垂直居中
    
    &:hover {                // & 代表父选择器 .nav-item,编译后为 .nav-item:hover
      background: #f5f5f5;
    }
    
    &.active {               // 编译后为 .nav-item.active
      color: @primary;
      border-bottom: 2px solid @primary;  // 底部指示条
    }
  }
  
  .nav-link {
    color: #333;
    text-decoration: none;   // 去掉下划线
    
    &:hover {
      color: @primary;
    }
    
    &::after {               // 伪元素:hover 时从下方滑出的动画线
      content: '';
      display: block;
      width: 100%;
      height: 2px;
      background: @primary;
      transform: scaleX(0);  // 初始状态:水平缩放为 0(不可见)
      transition: transform 0.3s;  // 过渡动画
    }
    
    &:hover::after {
      transform: scaleX(1);  // hover 时:水平缩放为 1(完全展开)
    }
  }
}
css
/* 编译后:嵌套规则被展开为后代选择器,& 替换为父选择器 */
.nav {
  background: #fff;
  height: 60px;
}
.nav .nav-item {
  display: inline-block;
  padding: 0 16px;
  line-height: 60px;
}
.nav .nav-item:hover {
  background: #f5f5f5;
}
.nav .nav-item.active {
  color: #1890ff;
  border-bottom: 2px solid #1890ff;
}
.nav .nav-link {
  color: #333;
  text-decoration: none;
}
.nav .nav-link:hover {
  color: #1890ff;
}
.nav .nav-link::after {
  content: '';
  display: block;
  width: 100%;
  height: 2px;
  background: #1890ff;
  transform: scaleX(0);
  transition: transform 0.3s;
}
.nav .nav-link:hover::after {
  transform: scaleX(1);
}

媒体查询嵌套

less
.container {
  width: 100%;
  padding: 0 @spacing-md;
  
  @media (min-width: 768px) {
    max-width: 720px;
    margin: 0 auto;
  }
  
  @media (min-width: 1024px) {
    max-width: 960px;
  }
  
  @media (min-width: 1280px) {
    max-width: 1200px;
  }
}
css
/* 编译后:媒体查询被提升到外层,不会嵌套 */
.container {
  width: 100%;
  padding: 0 16px;
}
@media (min-width: 768px) {
  .container {
    max-width: 720px;
    margin: 0 auto;
  }
}
@media (min-width: 1024px) {
  .container {
    max-width: 960px;
  }
}
@media (min-width: 1280px) {
  .container {
    max-width: 1200px;
  }
}

属性嵌套

less
.box {
  border: {
    top: 1px solid #ddd;
    right: 1px solid #ddd;
    bottom: 1px solid #ddd;
    left: 1px solid #ddd;
    radius: 4px;
  }
  
  margin: {
    top: 16px;
    bottom: 16px;
  }
  
  background: {
    color: #fff;
    image: url('bg.png');
    repeat: no-repeat;
    position: center;
  }
}
css
/* 编译后:属性嵌套展开为完整的属性名 */
.box {
  border-top: 1px solid #ddd;
  border-right: 1px solid #ddd;
  border-bottom: 1px solid #ddd;
  border-left: 1px solid #ddd;
  border-radius: 4px;
  margin-top: 16px;
  margin-bottom: 16px;
  background-color: #fff;
  background-image: url('bg.png');
  background-repeat: no-repeat;
  background-position: center;
}

Mixin(混合)

基本 Mixin

less
// 定义 mixin
.bordered {
  border: 1px solid #ddd;
  border-radius: 4px;
}

.box-shadow {
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.09);
}

.text-ellipsis {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

// 使用 mixin
.card {
  .bordered();
  .box-shadow();
  padding: 16px;
  
  .title {
    .text-ellipsis();
    font-size: 16px;
  }
}
css
/* 编译后:不带括号的 mixin 定义本身也会输出为普通类,使用处展开 mixin 样式 */
.bordered {
  border: 1px solid #ddd;
  border-radius: 4px;
}
.box-shadow {
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.09);
}
.text-ellipsis {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.card {
  border: 1px solid #ddd;
  border-radius: 4px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.09);
  padding: 16px;
}
.card .title {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 16px;
}

带参数的 Mixin

less
// 带参数
.size(@width, @height) {
  width: @width;
  height: @height;
}

.square(@size) {
  .size(@size, @size);
}

.rectangle(@width, @height: @width * 0.618) {
  .size(@width, @height);
}

// 使用
.avatar {
  .square(48px);
}

.banner {
  .rectangle(100%);
}
css
/* 编译后:参数被替换为实际传入的值 */
.avatar {
  width: 48px;
  height: 48px;
}

.banner {
  width: 100%;
  height: 61.8%;  /* 默认值:@width * 0.618 */
}

默认参数

less
.padding(@vertical: 8px, @horizontal: 16px) {
  padding: @vertical @horizontal;
}

.button-small {
  .padding(4px, 8px);
}

.button-default {
  .padding();              // 使用默认值 8px 16px
}

.button-large {
  .padding(12px, 24px);
}
css
/* 编译后:未传参时使用默认值 */
.button-small {
  padding: 4px 8px;
}

.button-default {
  padding: 8px 16px;  /* 使用默认值 */
}

.button-large {
  padding: 12px 24px;
}

可变参数

less
.box-shadow(@shadows...) {
  box-shadow: @shadows;
}

.card {
  .box-shadow(0 2px 4px rgba(0,0,0,0.1));
}

.card-hover {
  .box-shadow(0 2px 4px rgba(0,0,0,0.1), 0 4px 12px rgba(0,0,0,0.15));
}
css
/* 编译后:@shadows... 可变参数原样展开 */
.card {
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.card-hover {
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1), 0 4px 12px rgba(0, 0, 0, 0.15);
}

匹配模式

less
// 三角形
.triangle(@direction, @size, @color) {
  width: 0;
  height: 0;
  
  & when (@direction = top) {
    border-left: @size solid transparent;
    border-right: @size solid transparent;
    border-bottom: @size solid @color;
  }
  
  & when (@direction = bottom) {
    border-left: @size solid transparent;
    border-right: @size solid transparent;
    border-top: @size solid @color;
  }
  
  & when (@direction = left) {
    border-top: @size solid transparent;
    border-bottom: @size solid transparent;
    border-right: @size solid @color;
  }
  
  & when (@direction = right) {
    border-top: @size solid transparent;
    border-bottom: @size solid transparent;
    border-left: @size solid @color;
  }
}

.arrow-top {
  .triangle(top, 10px, #333);
}

.arrow-bottom {
  .triangle(bottom, 10px, #333);
}
css
/* 编译后:根据 @direction 参数只保留匹配的分支 */
.arrow-top {
  width: 0;
  height: 0;
  border-left: 10px solid transparent;
  border-right: 10px solid transparent;
  border-bottom: 10px solid #333;
}

.arrow-bottom {
  width: 0;
  height: 0;
  border-left: 10px solid transparent;
  border-right: 10px solid transparent;
  border-top: 10px solid #333;
}

条件判断

less
// when 条件
.generate-padding(@size) when (@size = sm) {
  padding: 4px 8px;
}

.generate-padding(@size) when (@size = md) {
  padding: 8px 16px;
}

.generate-padding(@size) when (@size = lg) {
  padding: 12px 24px;
}

.btn-sm { .generate-padding(sm); }
.btn-md { .generate-padding(md); }
.btn-lg { .generate-padding(lg); }

// 类型检查
.check(@value) when (isnumber(@value)) {
  width: @value;
}

.check(@value) when (iscolor(@value)) {
  color: @value;
}
css
/* 编译后:when 条件判断只输出匹配的分支 */
.btn-sm {
  padding: 4px 8px;
}

.btn-md {
  padding: 8px 16px;
}

.btn-lg {
  padding: 12px 24px;
}

命名空间

less
// 定义命名空间
#theme {
  .primary() {
    color: @primary;
    background: fade(@primary, 10%);
  }
  
  .success() {
    color: @success;
    background: fade(@success, 10%);
  }
  
  .danger() {
    color: @danger;
    background: fade(@danger, 10%);
  }
}

#utils {
  .flex-center() {
    display: flex;
    justify-content: center;
    align-items: center;
  }
  
  .flex-between() {
    display: flex;
    justify-content: space-between;
    align-items: center;
  }
  
  .ellipsis(@lines: 1) when (@lines = 1) {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  
  .ellipsis(@lines) when (@lines > 1) {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: @lines;
    overflow: hidden;
  }
}

// 使用命名空间
.alert-primary {
  #theme > .primary();
}

.center-box {
  #utils > .flex-center();
}

.title {
  #utils > .ellipsis(2);
}
css
/* 编译后:命名空间只是组织方式,使用时展开为普通 CSS */
.alert-primary {
  color: #1890ff;
  background: rgba(24, 144, 255, 0.1);
}

.center-box {
  display: flex;
  justify-content: center;
  align-items: center;
}

.title {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
}

函数

颜色函数

less
@base: #1890ff;

// 亮度调整
.lighter { color: lighten(@base, 20%); }
.darker { color: darken(@base, 20%); }

// 饱和度调整
.saturated { color: saturate(@base, 20%); }
.desaturated { color: desaturate(@base, 20%); }

// 色相调整
.hue-rotate { color: spin(@base, 60); }

// 混合颜色
.mixed { color: mix(@base, #ff0000, 50%); }

// 透明度
.fade { color: fade(@base, 50%); }
.fade-in { color: fadein(@base, 20%); }
.fade-out { color: fadeout(@base, 20%); }

// 灰度
.grayscale { color: greyscale(@base); }

// 对比色
.contrast { color: contrast(@base, #000, #fff); }
css
/* 编译后:颜色函数在编译期计算为具体颜色值 */
.lighter { color: #7ec1ff; }     /* lighten 20% */
.darker { color: #005cb1; }      /* darken 20% */
.saturated { color: #1890ff; }   /* saturate 20%(已饱和,无变化) */
.desaturated { color: #2f8fe8; } /* desaturate 20% */
.hue-rotate { color: #8718ff; }  /* spin 60° */
.mixed { color: #8c4880; }       /* mix 50% */
.fade { color: rgba(24, 144, 255, 0.5); }
.fade-in { color: #1890ff; }     /* fadein 20% */
.fade-out { color: rgba(24, 144, 255, 0.8); }
.grayscale { color: #8c8c8c; }
.contrast { color: #fff; }       /* 对比色 */

数学函数

less
.ceil { width: ceil(1.2px); }     // 2px
.floor { width: floor(1.8px); }   // 1px
.round { width: round(1.5px); }   // 2px
.abs { width: abs(-10px); }       // 10px
.max { width: max(10px, 20px); }  // 20px
.min { width: min(10px, 20px); }  // 10px
.percentage { width: percentage(0.5); }  // 50%
.sqrt { width: sqrt(9px); }       // 3px

字符串函数

less
@str: "hello";

.replace { content: replace(@str, "hello", "world"); }
.escape { content: escape(@str); }
.len { content: length(red, green, blue); }  // 3(返回列表元素个数)

自定义函数

less
// 自定义函数
.calc-rem(@px, @base: 16px) {
  @result: (@px / @base) * 1rem;
}

.title {
  .calc-rem(24px);
  font-size: @result;  // 1.5rem
}

// 列表操作
@colors: red, green, blue;

each(@colors, {
  .text-@{index} {
    color: @value;
  }
});
css
/* 编译后:自定义函数结果在编译期计算 */
.title {
  font-size: 1.5rem;
}

/* each 循环展开 */
.text-1 {
  color: red;
}
.text-2 {
  color: green;
}
.text-3 {
  color: blue;
}

循环

递归循环

less
// 生成栅格(使用 Flexbox 替代 float,更现代)
.generate-columns(@n, @i: 1) when (@i =< @n) {
  .col-@{i} {
    width: (@i / @n) * 100%;   // 计算每列宽度百分比
    box-sizing: border-box;     // 包含 padding 和 border
    padding: 0 15px;            // 列内边距
  }
  .generate-columns(@n, (@i + 1));  // 递归调用,i 递增
}

.generate-columns(12);  // 调用:生成 .col-1 到 .col-12

// 生成间距工具类
.generate-spacing(@n, @i: 0) when (@i =< @n) {
  .mt-@{i} { margin-top: (@i * 8px); }      // .mt-0, .mt-1 ... .mt-5
  .mb-@{i} { margin-bottom: (@i * 8px); }
  .ml-@{i} { margin-left: (@i * 8px); }
  .mr-@{i} { margin-right: (@i * 8px); }
  .pt-@{i} { padding-top: (@i * 8px); }
  .pb-@{i} { padding-bottom: (@i * 8px); }
  .pl-@{i} { padding-left: (@i * 8px); }
  .pr-@{i} { padding-right: (@i * 8px); }
  .generate-spacing(@n, (@i + 1));
}

.generate-spacing(5);  // 调用:生成 mt-0 到 mt-5 等
css
/* 编译后:循环展开为所有生成的类(这里展示部分) */
.col-1 {
  width: 8.33333333%;
  box-sizing: border-box;
  padding: 0 15px;
}
.col-2 {
  width: 16.66666667%;
  box-sizing: border-box;
  padding: 0 15px;
}
/* ... col-3 到 col-11 省略 ... */
.col-12 {
  width: 100%;
  box-sizing: border-box;
  padding: 0 15px;
}

.mt-1 { margin-top: 8px; }
.mb-1 { margin-bottom: 8px; }
/* ... mt-2 到 mt-4 省略 ... */
.mt-5 { margin-top: 40px; }
.mb-5 { margin-bottom: 40px; }

each 循环

less
// 遍历数组
@breakpoints: {
  sm: 576px;
  md: 768px;
  lg: 1024px;
  xl: 1280px;
}

each(@breakpoints, {
  .container-@{key} {
    max-width: @value;
    margin: 0 auto;
  }
});

// 遍历颜色
@colors: {
  primary: #1890ff;
  success: #52c41a;
  danger: #ff4d4f;
  warning: #faad14;
}

each(@colors, {
  .btn-@{key} {
    background: @value;
    color: #fff;
    
    &:hover {
      background: lighten(@value, 10%);
    }
  }
  
  .text-@{key} {
    color: @value;
  }
  
  .bg-@{key} {
    background-color: fade(@value, 10%);
    border-color: fade(@value, 30%);
  }
});
css
/* 编译后:each 循环遍历 map 生成所有类(这里展示 primary 部分) */
.container-sm {
  max-width: 576px;
  margin: 0 auto;
}
.container-md {
  max-width: 768px;
  margin: 0 auto;
}
/* ... 其他断点省略 ... */

.btn-primary {
  background: #1890ff;
  color: #fff;
}
.btn-primary:hover {
  background: #40a9ff;  /* lighten(#1890ff, 10%) */
}
.text-primary {
  color: #1890ff;
}
.bg-primary {
  background-color: rgba(24, 144, 255, 0.1);
  border-color: rgba(24, 144, 255, 0.3);
}
/* success、danger、warning 同理省略 */

导入

文件组织

src/
  styles/
    _variables.less    // 变量
    _mixins.less       // mixin
    _reset.less        // 重置样式
    _utilities.less    // 工具类
    components/
      _button.less
      _card.less
      _form.less
    themes/
      _light.less
      _dark.less
    main.less          // 主入口

导入语法

less
// main.less
@import '_variables';
@import '_mixins';
@import '_reset';
@import '_utilities';

// 导入组件
@import 'components/_button';
@import 'components/_card';
@import 'components/_form';

// 导入主题
@import 'themes/_light';

// 导入选项
@import (reference) '_mixins';    // 只引用,不输出
@import (once) '_variables';      // 只导入一次(默认)
@import (multiple) '_mixins';     // 允许重复导入
@import (inline) '_legacy.css';   // 只包含,不处理
@import (less) '_style.css';      // 当作 less 处理
@import (css) '_style.css';       // 当作 css 输出 @import

实际应用场景

主题切换

less
// 定义主题变量(使用 CSS 变量 + Less 变量结合)
@light-bg: #fff;
@light-text: #333;
@light-border: #ddd;
@light-primary: #1890ff;

@dark-bg: #141414;
@dark-text: #fff;
@dark-border: #444;
@dark-primary: #177ddc;

// 生成主题类
[data-theme="light"] {
  --bg: @light-bg;
  --text: @light-text;
  --border: @light-border;
  --primary: @light-primary;
}

[data-theme="dark"] {
  --bg: @dark-bg;
  --text: @dark-text;
  --border: @dark-border;
  --primary: @dark-primary;
}

// 使用 CSS 变量
body {
  background: var(--bg);
  color: var(--text);
}

.card {
  border: 1px solid var(--border);
  background: var(--bg);
}

响应式工具类

less
// 断点定义
@breakpoints: {
  sm: 576px;
  md: 768px;
  lg: 1024px;
  xl: 1280px;
  xxl: 1600px;
}

// 生成显示/隐藏类
each(@breakpoints, {
  @media (min-width: @value) {
    .d-@{key}-none { display: none !important; }
    .d-@{key}-block { display: block !important; }
    .d-@{key}-flex { display: flex !important; }
    .d-@{key}-inline { display: inline !important; }
    .d-@{key}-inline-block { display: inline-block !important; }
  }
});

// 生成列宽类
each(range(12), {
  @media (min-width: 768px) {
    .col-md-@{value} {
      width: (@value / 12) * 100%;
      box-sizing: border-box;
    }
  }
});

Vue 中使用 Less

基础配置

vue
<template>
  <div class="container">
    <button class="btn btn-primary">按钮</button>
    <div class="card">
      <h3 class="card__title">标题</h3>
      <p class="card__desc">描述</p>
    </div>
  </div>
</template>

<script setup>
// scoped 模式下不需要额外导入样式,class 直接使用即可
</script>

<style lang="less" scoped>
@import '@/styles/variables.less';

.container {
  padding: @spacing-lg;
}

.btn {
  padding: @spacing-sm @spacing-md;
  border: none;
  border-radius: @border-radius-base;
  cursor: pointer;
  
  &-primary {
    background: @primary;
    color: #fff;
    
    &:hover {
      background: lighten(@primary, 10%);
    }
  }
  
  &-danger {
    background: @danger;
    color: #fff;
  }
}

.card {
  border: 1px solid #eee;
  border-radius: @border-radius-lg;
  padding: @spacing-lg;
  
  &__title {
    font-size: @font-size-lg;
    margin-bottom: @spacing-sm;
  }
  
  &__desc {
    color: #666;
    font-size: @font-size-base;
  }
}
</style>

CSS Modules 方式

vue
<template>
  <div :class="styles.container">
    <button :class="[styles.btn, styles.btnPrimary]">按钮</button>
  </div>
</template>

<script setup>
import styles from './index.module.less'
</script>

<style lang="less" module>
@primary: #1890ff;

.container {
  padding: 24px;
}

.btn {
  padding: 8px 16px;
  border: none;
  border-radius: 4px;
}

.btnPrimary {
  background: @primary;
  color: #fff;
}
</style>

全局变量方案

js
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [vue()],
  css: {
    preprocessorOptions: {
      less: {
        // 全局注入变量(不需要每个文件 @import)
        additionalData: `@import "@/styles/variables.less";`
      }
    }
  }
})

常见问题

1. 变量名冲突

less
// ❌ 避免使用过于通用的变量名
@color: red;
@size: 10px;

// ✅ 使用有意义的前缀
@btn-color: red;
@icon-size: 10px;

2. mixin 输出多余代码

less
// ❌ 普通 mixin 会输出到 CSS
.bordered {
  border: 1px solid #ddd;
}

// ✅ 使用 () 使其成为函数,不输出
.bordered() {
  border: 1px solid #ddd;
}
css
/* 编译后对比:

   普通 mixin(无括号)会同时输出定义和使用处:
   .bordered {
     border: 1px solid #ddd;
   }
   .card {
     border: 1px solid #ddd;  // 重复
   }

   函数式 mixin(有括号)只输出使用处:
   .card {
     border: 1px solid #ddd;
   }
*/

3. 导入路径问题

less
// ❌ 相对路径容易出错
@import '../../../styles/variables.less';

// ✅ 使用别名(需配置 vite.config.ts)
@import '@/styles/variables.less';

4. 浏览器兼容

less
// Less 本身不处理浏览器兼容
// 需要配合 PostCSS 的 autoprefixer 使用

Less vs Sass 对比

特性LessSass/SCSS
变量符号@var$var
嵌套
Mixin.mixin()@mixin + @include
继承:extend()@extend
函数有限丰富
控制指令when + each@if@for@each@while
学习曲线较低较高
社区生态较小更大
推荐场景简单项目复杂项目

参考

个人学习笔记,部分内容借助 AI 辅助整理,仅供查阅参考,请以官方文档为准