Skip to content

Less 的使用与Less 语法(Day19 2/3) ​

本页对应 Day19_PC项目&Less,共 5 段视频,约 1 小时 22 分。

学习目标 ​

  • 能使用 Less 定义普通变量及含特殊符号的变量值(如 ~"..."),并在属性值中直接引用,编译结果正确。
  • 能区分变量作为属性值、属性名和选择器三种使用场景,正确使用 @var 或 @{var},并通过编译结果验证。
  • 能编写无参、有参、带默认值的 Less 混合,使用顺序传参和命名传参调用,并在调用处追加其他样式。
  • 能使用 arguments 获取混合实参列表,编写带浏览器私有前缀的 box-shadow 混合并验证生成的 CSS。
  • 能利用同名混合与参数值匹配实现 Less 条件判断,按 up/down/left/right 生成对应方向的纯 CSS 三角形。
  • 能完成页面导航与路径导航的 HTML 结构和 Less 样式,使用 flex 布局、伪元素生成分隔符,并用 :not(:last-child)::after 排除最后一项且通过页面效果验证。

知识点 ​

一、Less 变量:把常用值先存起来 ​

为什么需要:页面里颜色、长度会反复出现,散写难维护。转写稿先演示“左边 Less、右边 CSS”的对比,就是让变量集中管理常用值。
怎么想:变量用 @变量名: 值; 定义,使用时直接写 @变量名。
怎么做:

less
@len: 200px;
@master-red: #e60012;
@master-green: #080;

.box {
  width: @len;
  color: @master-red;
  background: @master-red;
  border-color: @master-green;
}

易错点:变量名和值之间是冒号;变量名前必须有 @。转写稿说 Less 变量名没有 JS 那样的硬性命名规范,但仍建议取有意义的名字,如 @master-red、@len。

二、变量当属性名或选择器:要加大括号 ​

为什么需要:不仅属性值想复用,属性名、选择器也可能想参数化。
怎么想:变量作为属性值可直接用;作为属性名或选择器,Less 编译时无法判断成分,需要 @{变量名}。
怎么做:

less
@prop: background-position;
@sel: .item;

@{sel} {
  @{prop}: 10px 10px;
}

易错点:转写稿调试时写 @prop: 10px 10px; 保存后 CSS 里根本没有 background-position,改成 @{prop} 才编译。选择器同理,直接 @sel { ... } 不生效,要写 @{sel}。

三、变量值含特殊符号:用 ~"..." 包起来 ​

为什么需要:媒体查询条件里常有冒号、空格,如 (min-width: 768px);直接写会与“变量名:值”的冒号冲突。
怎么想:把整段值用波浪号加双引号包住,告诉 Less 里面是特殊字符串。
怎么做:

less
@min768: ~"(min-width: 768px)";

@media @min768 {
  .box { width: 100%; }
}

易错点:不包起来时,转写稿里出现“冒号分不清哪个是变量名”的编译冲突。空格、冒号这类特殊符号要按此处理。

四、混合:复用一组样式 ​

为什么需要:类似 JS 自定义函数,把定位居中、阴影等一组样式复用。
怎么想:定义混合时在名字后加小括号,避免它本身被编译成类选择器;调用时也加括号。
怎么做:

less
.center-box(@w: 400px, @h: 300px) {
  position: absolute;
  width: @w;
  height: @h;
  left: 50%;
  top: 50%;
  margin-left: -(@w / 2);
  margin-top: -(@h / 2);
}

.item {
  .center-box(600px, 500px);
  background: #ccc;
}

易错点:转写稿中不加小括号时,混合会同时被当成类选择器输出;加了 () 后“不调用不编译”。带参混合少传、多传都会报错;除法要加括号,如 -(@w / 2)。默认参数在顺序传参时建议放后面;若放前面,只传一个会报错,按名字传参才更稳。调用也可按名字传参,如 .center-box(@h: 600px, @w: 800px);。

五、arguments 与条件判断 ​

为什么需要:arguments 可取得实参列表,转写稿用它演示过 box-shadow 私有前缀兼容写法;条件判断则让 Less 根据参数值选择混合。
怎么做:

less
.box-shadow(@x, @y, @b, @o, @c) {
  box-shadow: @arguments;
  -webkit-box-shadow: @arguments;
  -moz-box-shadow: @arguments;
}

.triangle(@w, @c, @dir) when (@dir = up) {
  width: 0; height: 0;
  border-width: @w;
  border-style: solid;
  border-color: transparent transparent @c transparent;
}
.triangle(@w, @c, @dir) when (@dir = down) {
  width: 0; height: 0;
  border-width: @w;
  border-style: solid;
  border-color: @c transparent transparent transparent;
}

易错点:arguments 得到的是空格分隔的实参列表;转写稿强调它不像 JS 数组那样可随便遍历。条件判断本质是写多个同名混合,用 when 匹配第三个参数;传 down 不会调用 up 的混合,要再写 down 版本,类似 switch case。

六、在页面导航、路径导航中的调试 ​

页面导航用 Less 配合伸缩盒:给容器定高后,子项在侧轴默认拉伸,能一起得到高度。路径导航里,转写稿用伪元素做斜杠,最后一个不能有斜杠,于是写成 .path span:not(:last-child)::after 排除最后一项;span 换行产生的空隙,则把父级设为 display:flex 消掉。调试时先看生成的选择器,再按“后代—伪类—伪元素”的顺序拆解,避免把组合选择器看混。

一、为什么需要 Less 变量 ​

原生 CSS 里,一个主题色、一个版心宽度、一个常用长度,往往要在很多地方重复写。改一次就要全局搜索替换,容易漏改。Less 变量就是把这些“常用值”先存起来,用的时候再取。转写稿里反复强调:变量最常见的用途是存颜色、长度,使用时直接作为属性值。先想清楚“这个值会不会复用”,再决定是否定义变量;不要把只用一次、语义不清的值也塞成变量。

二、变量的定义与使用 ​

定义变量用 @变量名: 值;,冒号左边是变量名,右边是值。普通值直接写;值里如果有冒号、空格等特殊符号,要用波浪号加双引号包起来,避免 Less 编译时把特殊符号误认为语法分隔符。

less
@len: 200px;
@master-red: #e60000;
@master-green: #008000;

.box {
  width: @len;
  color: @master-red;
  background: @master-red;
}

@min768: ~"(min-width: 768px)";
@media @min768 {
  .box { width: 100%; }
}

怎么想:变量作为属性值时,直接 @变量名 使用;它既可以是单个属性值,也可以出现在复合属性中。易错点:值里有特殊符号却不用 ~"",编译会冲突;变量名虽然转写稿说“没有命名规范、随便叫”,但实际项目仍建议语义化,别用 a、b 这类名字。

三、变量作属性名和选择器 ​

变量不仅能存属性值,还能存属性名、选择器。但这时不能直接写 @prop,要用 @{prop} 包起来。转写稿里演示:把 background-position 存进变量,直接写 @prop: ... 不会编译成属性名;写成 @{prop}: 10px 10px; 才生效。选择器同理。

less
@prop: background-position;
@sel02: ~".news .item";

.item {
  @{prop}: 10px 10px;
}

@{sel02} {
  color: @master-green;
}

怎么想:Less 编译时需要判断 @ 后面是“取值”还是“拼名字”。作为属性值可直接取;作为属性名、选择器则要用 @{} 明确告诉编译器“这里要替换成名字”。易错点:选择器变量值里有空格时,也要用 ~"" 包起来,否则会引发语法错误。

四、混合:把重复样式封装起来 ​

混合类似 JS 自定义函数:把一段样式封装起来,需要时调用。无参混合可以写成类选择器形式,但建议在名字后加小括号,避免它本身被编译成 CSS 类。

less
.center-box() {
  position: absolute;
  width: 400px;
  height: 300px;
  left: 50%;
  top: 50%;
  margin-left: -200px;
  margin-top: -150px;
}

.item {
  .center-box();
  background: #fff;
}

怎么想:先找重复样式,再抽成混合;调用后还可以继续追加当前元素自己的样式。易错点:不加小括号时,混合名本身也会作为类选择器输出到 CSS;不调用带参混合时,它不会编译,报错是正常的。

五、带参数、默认值与 @arguments ​

带参混合把变化的部分变成形参。调用时既可“按顺序传参”,也可“按名字传参”。有默认值的参数,按顺序传参时建议放后面;若放前面,按顺序传参容易错位,按名字传参则不受顺序限制。

less
.center-box02(@w, @h) {
  width: @w;
  height: @h;
  left: 50%;
  top: 50%;
  margin-left: -(@w / 2);
  margin-top: -(@h / 2);
}

.center-box03(@w: 1000px, @h: 400px) {
  width: @w;
  height: @h;
}

.a { .center-box02(600px, 500px); }
.b { .center-box02(@h: 600px, @w: 800px); }
.c { .center-box03(1200px); }

@arguments 表示实参列表,转写稿用它演示过私有前缀兼容写法:把参数列表直接塞进 box-shadow 等属性中。易错点:参数个数不对、少传多传都会编译失败;除法要加小括号,如 -(@w / 2);@arguments 得到的是空格分隔的列表,不要想当然当成数组去遍历。

六、条件判断:用同名混合做分支 ​

Less 没有 if/else,转写稿明确说它靠“混合 + when 条件”做判断。做法是写多个同名混合,用 when (@direction = up) 这类条件匹配不同实参。比如用纯 CSS 做三角形:宽高为 0,只留边框,把不需要的三个边框设为透明。

less
.triangle(@width, @color, @direction) when (@direction = up) {
  width: 0; height: 0;
  border-style: solid;
  border-width: @width;
  border-color: transparent transparent @color transparent;
}
.triangle(@width, @color, @direction) when (@direction = down) {
  width: 0; height: 0;
  border-style: solid;
  border-width: @width;
  border-color: @color transparent transparent transparent;
}

.arrow { .triangle(20px, #080, up); }

怎么想:把“方向”作为分支条件,四个方向写四个同名混合。易错点:传入的方向没有对应 when 就不编译;条件里的变量名、等号、方向单词要写对;这种写法像 switch-case,但比 JS 啰嗦,适合少量分支。

七、案例:页面导航与路径导航 ​

页面导航案例中,转写稿用 Flex 做布局:外层 .container 设 height: 50px; display: flex;,左侧分类设背景色、宽度、文字居中,右侧导航设 display: flex,链接设 margin: 0 22px; color: #333; font-size: 16px;。路径导航即“面包屑导航”,用伪元素生成斜杠,并排除最后一项:

less
.path span:not(:last-child)::after {
  content: "/";
  color: #ccc;
  margin: 0 5px;
}
.path span { display: flex; }

调试时注意:版心外层可加 min-width: 1200px,防止视口变窄后背景色断开;span 是行内元素,换行会产生空格,改成伸缩项目可消除多余间距。易错点:伪元素要加在 span 上而不是整个 .path 上;最后一个 span 要用 :not(:last-child) 排除,否则末尾多一个斜杠。

课堂画面 ​

课堂画面 1

课堂画面 2

课堂画面 3

课堂画面 4

课堂画面 5

代码示例 ​

示例 1:Less 变量(一般形式 / 特殊符号 / 作为属性名与选择器) ​

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Less 变量</title>
<style type="text/less">
/* ① 一般形式:@变量名: 值;  冒号左边是名字,右边是值 */
@len: 100px;
@master-red: #e1251b;
@master-green: #080;
@border-width: 2px;

/* ② 值里带特殊符号(冒号、空格、括号)要用 ~"..." 包起来 */
@min768: ~"(min-width: 768px)";
@sel02: ~".item .title";

/* ③ 想把属性名、选择器存进变量 */
@prop: background-position;
@sel01: .item;

.box {
  width: @len;
  height: @len;
  color: @master-red;
  background-color: @master-green;
  border: @border-width solid @master-red;

  /* 变量作属性名:必须 @{变量名} */
  @{prop}: 10px 10px;

  /* 变量作选择器:同样必须 @{变量名} */
  @{sel01} {
    width: (@len / 2);   /* 除法要自己加小括号 */
  }

  @{sel02} {
    color: @master-red;
  }
}

@media @min768 {
  .box { width: 200px; }
}
</style>
<script src="https://cdn.jsdelivr.net/npm/less@4"></script>
</head>
<body>
  <div class="box">
    <div class="item"><div class="title">item / title</div></div>
  </div>
</body>
</html>

要点解释 / 易错点

  • 定义变量固定格式:@变量名: 值;,冒号是变量名和值的分界符;Less 变量名没有任何命名规范,@master-red、@a 都能用。
  • 变量作为属性值时直接 @len 使用;作为属性名或选择器时必须写成 @{prop}、@{sel01},不加大括号不会被编译出来(转写稿里 @prop: 10px 10px; 直接写就没有任何输出)。
  • 值里出现特殊符号(冒号、空格等,例如媒体查询条件)要用 ~"..." 包起来,否则会和变量定义的分号/冒号冲突,编译报错。
  • 除法 @len / 2 必须用小括号包住,Less 新版本不会自动把 / 当除法算。

示例 2:Less 混合(无参 / 有参 / 默认值 / @arguments / 条件判断) ​

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Less 混合与条件判断</title>
<style type="text/less">
/* ① 基本混合:名字后面加 (),不调用就不编译 */
.center-box01() {
  position: absolute;
  width: 400px;
  height: 300px;
  left: 50%;
  top: 50%;
  margin-left: -200px;
  margin-top: -150px;
}

/* ② 带参数的混合 */
.center-box02(@width, @height) {
  position: absolute;
  width: @width;
  height: @height;
  left: 50%;
  top: 50%;
  margin-left: -(@width / 2);
  margin-top: -(@height / 2);
}

/* ③ 带默认值的混合 */
.center-box03(@width: 100px, @height: 400px) {
  position: absolute;
  width: @width;
  height: @height;
  left: 50%;
  top: 50%;
  margin-left: -(@width / 2);
  margin-top: -(@height / 2);
}

/* ④ @arguments:拿到全部实参组成的列表(空格分隔) */
.box-shadow(@x, @y, @b, @o, @color) {
  box-shadow: @arguments;
  -webkit-box-shadow: @arguments;
}

/* ⑤ 条件判断:同名混合 + when 守卫,相当于 switch-case */
.triangle(@w, @c, @direction) when (@direction = up) {
  width: 0;
  height: 0;
  border-style: solid;
  border-width: @w;
  border-color: transparent transparent @c transparent;   /* 下边框有色 → 箭头朝上 */
}
.triangle(@w, @c, @direction) when (@direction = down) {
  width: 0;
  height: 0;
  border-style: solid;
  border-width: @w;
  border-color: @c transparent transparent transparent;   /* 上边框有色 → 箭头朝下 */
}
.triangle(@w, @c, @direction) when (@direction = left) {
  width: 0;
  height: 0;
  border-style: solid;
  border-width: @w;
  border-color: transparent @c transparent transparent;   /* 右边框有色 → 箭头朝左 */
}
.triangle(@w, @c, @direction) when (@direction = right) {
  width: 0;
  height: 0;
  border-style: solid;
  border-width: @w;
  border-color: transparent transparent transparent @c;   /* 左边框有色 → 箭头朝右 */
}

/* 调用混合 */
.item01 { .center-box02(600px, 500px); background-color: pink; }          /* 按顺序传参 */
.item02 { .center-box02(@height: 600px, @width: 800px); background-color: skyblue; } /* 按名字传参 */
.item03 { .center-box03(1000px); background-color: #ddd; }               /* 只传一个,高度用默认值 */
.item04 { width: 200px; height: 100px; background: #fff; .box-shadow(3px, 10px, 15px, 0px, #ccc); }
.item05 { .triangle(20px, #080, up); }
.item06 { .triangle(20px, #080, down); }
.item07 { .triangle(20px, #080, left); }
.item08 { .triangle(20px, #080, right); }
</style>
<script src="https://cdn.jsdelivr.net/npm/less@4"></script>
</head>
<body>
  <div class="item01">按顺序传参</div>
  <div class="item02">按名字传参</div>
  <div class="item03">默认值生效</div>
  <div class="item04">@arguments</div>
  <div class="item05"></div>
  <div class="item06"></div>
  <div class="item07"></div>
  <div class="item08"></div>
</body>
</html>

要点解释 / 易错点

  • 混合名后一定要加 (),否则它会被当成类选择器编译进 CSS;加了 () 就“不调用不执行”,和 JS 函数一样。
  • 有参混合传参有两种:按顺序(600px, 500px)和按名字(@height: 600px, @width: 800px),按名字传参时顺序无所谓。
  • 少传一个、多传一个参数都会报错(不编译),必须一一对应。
  • 默认值用冒号不用等号:@height: 400px。按顺序传参时,有默认值的参数必须写在后面;按名字传参时放前放后都可以。
  • @arguments 就是全部实参组成的列表,空格分隔,适合一次性塞给 box-shadow 这类多值属性(老式加私有前缀的兼容写法)。
  • Less 没有 if 语法,条件判断靠同名混合 + when 守卫实现,传 up/down/left/right 匹配不同的混合,相当于 switch-case;一个都匹配不上就什么都不输出。

示例 3:页面导航 + 路径导航(Less 嵌套 + Flex + 伪元素面包屑) ​

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>页面导航 + 路径导航</title>
<style type="text/less">
@master-red: #e1251b;
@border-width: 2px;

* { margin: 0; padding: 0; }
a { text-decoration: none; color: #333; }

.container {
  width: 1200px;
  margin: 0 auto;
}

/* ---------- 页面导航 ---------- */
.nav-bar {
  min-width: 1200px;
  border-bottom: @border-width solid #ccc;

  .container {
    height: 50px;
    line-height: 50px;   /* 行高给容器,后代继承 */
    display: flex;
  }

  .category {
    width: 210px;
    background-color: @master-red;
    h2 {
      text-align: center;
      font-size: 14px;
      color: #fff;
    }
  }

  .nav {
    display: flex;
    a {
      margin: 0 22px;
      font-size: 16px;
      color: #333;
    }
  }
}

/* ---------- 路径导航(面包屑) ---------- */
.product {
  margin-top: 15px;

  .pass-name {
    display: flex;          /* span 变伸缩项目,消除换行产生的空白 */
    padding: 10px 0;

    span {
      &::after {
        content: "/";
        color: #ccc;
        margin: 0 5px;
      }
      /* 最后一个 span 不生成斜杠 */
      &:last-child::after {
        content: "";
        margin: 0;
      }
    }
  }
}
</style>
<script src="https://cdn.jsdelivr.net/npm/less@4"></script>
</head>
<body>
  <div class="nav-bar">
    <div class="container">
      <div class="category">
        <h2>全部商品分类</h2>
      </div>
      <div class="nav">
        <a href="#">手机数码</a>
        <a href="#">电脑办公</a>
        <a href="#">家用电器</a>
        <a href="#">服装鞋包</a>
      </div>
    </div>
  </div>

  <div class="product">
    <div class="container">
      <div class="pass-name">
        <span><a href="#">手机数码</a></span>
        <span><a href="#">手机通讯</a></span>
        <span><a href="#">Apple 苹果</a></span>
        <span>iPhone 6S</span>
      </div>
    </div>
  </div>
</body>
</html>

要点解释 / 易错点

  • 高度只给 .container 写 50px 就够:它是伸缩容器,.category 和 .nav 作为伸缩项目在侧轴(竖向)默认拉伸,高度自动跟容器一致。
  • 行高写在 .container 上会被后代继承,h2 和 a 不用再单独写 line-height。
  • 面包屑的斜杠用 ::after 伪元素生成,&::after 里的 & 表示“当前选择器”(即 span),编译出来是 .pass-name span::after;漏写 & 就变成后代选择器了。
  • 最后一个 span 要去掉斜杠,用 &:last-child::after { content: ""; } 覆盖掉 content: "/",选择器一层层拆开读就不容易晕。
  • span 是行内元素,HTML 换行会留下空格造成间距偏大,把 .pass-name 设为 display: flex 让 span 变伸缩项目,多余空白直接消失。
  • 页面主体给了 min-width: 1200px:窗口比版心窄时不会出现背景色断掉的情况(版心本身是固定 1200px)。

常见错误 ​

  1. 现象:变量存属性名或选择器时,编译结果不生效或选择器错乱。原因:把变量当普通属性值直接使用。修法:属性名/选择器场景改为 @{变量名}。
  2. 现象:变量值含冒号、空格等符号时编译报错或解析错位。原因:特殊符号未转义包裹。修法:值写成 ~"..." 形式。
  3. 现象:定义混合后未调用,CSS 里却多出一个类选择器。原因:混合名后未加 ()。修法:混合定义名后补 (),只在调用时编译。
  4. 现象:调用带参混合时报错或不编译。原因:少传、多传,或按顺序传参时默认值参数放在前面。修法:参数个数对齐;默认值参数放后,或改用按名字传参。
  5. 现象:路径导航中最后一项仍出现斜杠,或空白区域也可点击。原因:伪元素加在 a 上且未排除最后一项;可点击范围未收窄到文字。修法:伪元素加给内层 span,用 :not(:last-child)::after 排除最后一项。
  6. 现象:窗口宽度小于版心时,外层背景色缺失或布局溢出。原因:外层容器未设最小宽度。修法:给相关外层容器设置 min-width 为版心宽度。

练习 ​

  1. 定义长度、主题色变量,并分别用于 width、color、background;再用变量存一个属性名和一个选择器,验证 @{} 写法。
  2. 定义两个变量:一个存媒体查询条件,一个存带空格的选择器;用 ~"..." 处理特殊符号并成功编译。
  3. 写一个无参混合实现元素水平垂直居中,再改写为带宽高参数的混合;分别用顺序传参和按名字传参调用。
  4. 写一个带默认值的混合,故意把默认值参数放前面,分别测试顺序传参和按名字传参的编译结果。
  5. 用同名混合实现三角形方向判断:传入 up/down/left/right 时输出对应边框颜色与透明边框,并说明它对应哪种分支结构。

原视频对照 ​

#视频时长
1Less变量15:42
2页面导航07:17
3Less混合26:00
4路径导航17:36
5Less条件判断15:47

小结 ​

  1. Less 变量既能存值,也能在特殊场景下参与属性名、选择器拼接,但写法不同。
  2. 变量值里的特殊符号必须显式处理,否则会干扰 Less 解析。
  3. 混合定义时加 () 更符合“函数”语义,可避免无调用却被编译。
  4. 带参混合优先保证参数个数与传参方式匹配;默认值参数放后更稳妥。
  5. arguments 适合获取实参列表,但现代工程中不应再依赖它做兼容前缀库。
  6. Less 的条件判断依赖同名混合匹配,本质更接近分支匹配而非真正 if。
  7. 页面导航、路径导航这类布局要同时关注结构层级、可点击区域、最后一项排除和最小宽度。
  8. 复杂选择器要按“后代/伪类/伪元素”逐段拆读,不能只凭整体感觉判断。