Less 语言的语法规则及使用
一. Less 介绍
Less(Leaner Style Sheets) 是一种向后兼容的 CSS 扩展语言,属于 CSS 预处理器的范畴,它扩展了 CSS 语言,增加了变量、混入、函数等特性,使 CSS 更易维护和扩展。
Less 官网:lesscss
Less 中文官网:lesscss.cn
Less 既可以在客户端上运行 ,也可以借助 Node.js 在服务端运行。
直接在浏览器端使用:
- 引入
.less
文件,(注意 rel 属性设置为"stylesheet/less"
)
<link rel="stylesheet/less" type="text/css" href="styles.less" />
- 引入
less.js
文件
<script src="https://cdn.jsdelivr.net/npm/less"></script>
在服务器端 Node.js 中使用:
- 安装 Less 编译器
npm install less -g
npm install less@1.6.2 -g #安装1.6.2版本
npm install less@latest -g #安装最新版本
- 编译
less
文件并输出标准格式的css
文件
lessc styles.less styles.css
在 VScode 中的使用:
- 安装插件:
Easy LESS
- 创建
styles.less
并按照 less 的语法规则编写 less 代码 - 保存后自动生成
styles.css
文件 - 在 html 页面中引入编译好的
styles.css
文件
二. Less 的语法规则
1. 变量(Variables)
1.1 变量的定义与使用
在 Less 语言中可以定义变量,用@
符来修饰,格式:@name:value
-
对于比较常用的属性值,可以先使用变量进行定义,之后进行多次调用。
-
这里的变量既可以定义属性值,也可以定义选择器和属性名,作为属性值调用时使用
@name
,作为选择器和属性名调用时使用@{name}
。 -
变量还可以定义 url 地址,调用时使用
@{name}
作为属性值调用:
@width:100px; //作为属性值使用
@height:@width + 100px;
.class{
width: @width;
height: @height;
}
//编译结果:
.class{
width: 100px;
height: 200px;
}
作为选择器和属性名调用:
@selector: #id; //作为选择器使用
@m: margin; //作为属性名使用
@{selector} {
@{m}:10px;
}
//编译结果
#id {
margin: 10px;
}
作为 url 地址调用:
@url: './img'; //作为url使用
.class {
background-color: url('@{url}/image.png') //特别注意这边是字符串
;
}
//编译结果:
.class {
background-color: url('./img/image.png');
}
1.2 变量的作用域
Less 中变量的作用域规则与 CSS 中相同,都是从本规则集的变量和混入中进行查找,若没有,则从父集作用域继承。
@color: red;
#page {
@color: white;
#header {
color: @color; // white
}
}
1.3 变量的延迟加载(Lazy evaluation)
Lazy evaluation:延迟计算,惰性计算,指的是仅仅在真正需要执行的时候才计算表达式。
-
在 Less 中支持变量的延迟加载,即变量的声明可以位于调用之后;
-
在同一个作用域中,若一个变量被声明了多次,则最后一次声明有效;在不同的作用域中声明同一个变量,则根据作用域的查找规则进行赋值。
.lazy-eval {
width: @var; //width: 9%
@a: 9%;
}
@var: @a;
@a: 100%;
2. 混入(Mixins)
混入是将一组属性从一个规则集引入到另外一个规则集的方式
2.1 常规混入
- 在一个规则集中使用另外一个规则集中的全部属性:
selector()
.border {
border-top: dotted 1px black;
border-bottom: solid 2px black;
}
.cls {
.border(); //这里的括号加不加都可以
}
编译的 CSS 代码:
.border {
border-top: dotted 1px black;
border-bottom: solid 2px black;
}
.cls {
border-top: dotted 1px black;
border-bottom: solid 2px black;
}
- 可以定义隐式的混入,即可以供其他规则集复用,但该规则集不会出现在 CSS 代码中
.border() {
border-top: dotted 1px black;
border-bottom: solid 2px black;
}
.cls {
.border();
}
编译的 CSS 代码:
.cls {
border-top: dotted 1px black;
border-bottom: solid 2px black;
}
2.2 带参数的混入
- 可以定义带参数的混入,参数可以设置默认值。当定义多个参数时,参数之间可以用分号或逗号分隔,但要统一,混合使用逗号和分号会造成参数赋值错误
例如:
.mixin(@color, @padding:10px, @margin:2px) {
color: @color;
padding: @padding;
margin: @margin;
}
.cls1 {
.mixin(red, 2px, 10px); //传入了三个参数
}
.cls2 {
.mixin(red, 2px, 0 10px 0 10px);
}
.cls3 {
.mixin(1, 2, 3; 4, 5); //传入了两个参数
}
.cls4 {
.mixin(1, 2, 3; ); //传入了一个参数
}
编译的 CSS 代码:
.cls1 {
color: red;
padding: 2px;
margin: 10px;
}
.cls2 {
color: red;
padding: 20px;
margin: 0 10px 0 10px;
}
.cls3 {
color: 1, 2, 3;
padding: 4, 5;
margin: 2px;
}
.cls4 {
color: 1, 2, 3;
padding: 10px;
margin: 2px;
}
- 当定义了多个相同名称的混入时,
less
会根据参数的情况自动匹配混入
.mixin(@color) {
color-1: @color;
}
.mixin(@color, @padding: 2px) {
color-2: @color;
padding-2: @padding;
}
.cls1 {
.mixin(#fff); // 调用了第一个mixin和第二个mixin,因为第二个mixin的第二个参数带默认值
}
.cls2 {
.mixin(#fff, 5px); // 只能调用第二个mixin
}
编译的 CSS 代码:
.cls1 {
color-1: #fff;
color-2: #fff;
padding-2: 2px;
}
.cls2 {
color-2: #fff;
padding-2: 5px;
}
- 可以使用命名参数进行混入的传参,这样引用 mixin 时可以通过参数名称而不是参数位置来为 mixin 提供参数
.mixin(@color: black; @margin: 10px; @padding: 20px) {
color: @color;
margin: @margin;
padding: @padding;
}
.cls {
.mixin(@margin: 20px; @color: #fff);
}
编译的 CSS 代码
.cls {
color: #fff;
margin: 20px;
padding: 20px;
}
- 可以使用
@arguments
变量接收参数
.border(@x: solid; @c: red) {
border: 21px @arguments;
}
.cls {
.border(solid, black);
}
编译后的 CSS 代码
.cls {
border: 21px solid black;
}
2.3 分组混入
- 可以对混入进行分组,在调用的时候选择需要混入的属性分组即可。
假设你想在 #bundle
下打包一些混入和变量,以供以后重用或分发:
#bundle() {
.button {
display: block;
border: 1px solid black;
background-color: grey;
&:hover {
background-color: white;
}
}
.tab {
border: 1px solid red;
background-color: blue;
}
.citation {}
}
.cls{ // 在.cls中混入#bundle中的.button类
#bundle.button();
}
.cls2{
#bundle.tab();
}
编译后的 CSS 如下:
.cls {
display: block;
border: 1px solid black;
background-color: grey;
}
.cls:hover {
background-color: white;
}
.cls2 {
border: 1px solid red;
background-color: blue;
}
2.4 映射混入
- 可以指定引用混入中的特定属性值,使用
mixin[property]
#colors() {
color: blue;
border-color: green;
}
.button {
color: #colors[color];
border: 1px solid #colors[border-color];
}
编译后的 CSS 代码:
.button {
color: blue;
border: 1px solid green;
}
3. 嵌套(Nesting)
- Less 中可以使用嵌套代替级联,或者将嵌套与级联进行结合使用。使用嵌套的书写方式代码更加简洁,层级更加清晰。
#header {
color: black;
}
#header .navigation {
font-size: 12px;
}
#header .logo {
width: 300px;
}
对应的 Less 写法如下:
#header {
color: black;
.navigation {
font-size: 12px;
}
.logo {
width: 300px;
}
}
- 另外可以通过
&
来表示当前选择器的父级选择器,clearfix hack
的 Less 写法如下:
.clearfix {
display: block;
zoom: 1;
&:after {
content: ' ';
display: block;
font-size: 0;
height: 0;
clear: both;
visibility: hidden;
}
}
- 嵌套实现下的
@
规则
诸如 @media 或 @supports 之类的 @ 规则可以与选择器相同的方式嵌套。
.component {
width: 300px;
@media (min-width: 768px) {
width: 600px;
@media (min-resolution: 192dpi) {
background-image: url(/img/image.png);
}
}
@media (min-width: 1280px) {
width: 800px;
}
}
对应的 CSS 输出:
.component {
width: 300px;
}
@media (min-width: 768px) {
.component {
width: 600px;
}
}
@media (min-width: 768px) and (min-resolution: 192dpi) {
.component {
background-image: url(/img/retina2x.png);
}
}
@media (min-width: 1280px) {
.component {
width: 800px;
}
}
4. 运算操作(Operations)
4.1 数学运算
Less 中支持+
、-
、*
、/
等运算操作,可以对任意的数字、变量、颜色进行变换。
数学运算会考虑单位,如果单位不统一,则首先根据第一个明确指出的单位进行数据转换,之后在进行数值计算,如果转换不可能或没有意义,则忽略单位。
@operation-1: 1 + 0px; // result is 1px
@operation-2: 10mm + 5cm; // result is 60mm
@operation-3: 2px - 3cm; // result is -111.38582677px
@base: 5%;
@filler: @base * 2; // result is 10%
@other: @base + @filler; // result is 15%
@base: 2cm * 3mm; // result is 6cm
4.2 颜色运算
Less 中可以对颜色进行运算操作,还可以使用颜色的函数;
注意: 在进行/
运算时必须加括号,否则无法进行计算。
@color: (#224488 / 2); // result is #112244
@color: #224488 / 2; //result is #224488 / 2
@background-color: #112244 + #111; // result is #223355
4.3 calc()运算
为了 CSS 兼容性,calc()函数不计算数学表达式的值,但会计算嵌套函数中的变量。
@var: 50vh/2;
width: calc(50% + (@var - 20px)); // result is calc(50% + (25vh - 20px))
5. 转义(Escaping)
在 Less 中可以使用任意自定义的字符串变量作为属性值、属性名或选择器。~"anything"
或 ~'anything'
中的任何内容都按原样使用,除了插值之外没有任何变化。
一般情况下我们是不需要用到转义的,只有在代码不能被正常编译的情况下,才需要使用转义。
例如border-radius
属性,在 CSS 中我们可以使用斜杆/
来设置属性值,/
前面的是水平半径,后面的是垂直半径。但是在 Less 中不支持使用/
,所以需要用到转义字符进行转义。
.box {
width: 100px;
height: 100px;
border: 1px solid #000;
border-radius: 0 25px 25px 0 ~'/' 0 25px 25px 0;
}
6. 函数(Functions)
Less 提供了许多函数,可以转换颜色、操作字符串和进行数学运算。函数手册
- 颜色函数:
rgb()
、rgba()
、img-size()
、lighten()
、darken()
等 - 字符串函数:
escape()
、%格式化
、replace()
等
7. 注释(Comments)
/* 块注释
* 块注释会被保留在css文件中 */
@var: red;
// 行注释,不会保留在css中
@var: white;
8. 导入(Import)
使用@import "xxx"
的形式可以将其他样式文件导入到本样式文件中,导入后可以直接使用里面的样式和变量
- 如果是
.less
文件,则可以省略文件后缀,例如@import "style"
- 如果是
.css
文件,则不可以省却文件后缀
三. Less 与 CSS 的区别
- Less 是 CSS 预处理语言,拓展了 CSS 的方法,不能直接被浏览器识别,需要 Less 预处理器编译成 CSS 语言之后才能识别。
- Less 支持定义变量,通过
@
显式声明一个变量后,可以实现对这个变量的多次的复用。而在 CSS 中不支持定义变量,对于重复出现的属性名和属性值则需要重复书写。
- 局部变量
.cls {
--bg-color: blue;
background-color: var(--bg-color);
}
- 全局变量
:root {
--global-color: blue;
}
.cls {
background-color: var(--global-color);
}
- Less 支持 mixin 混入,可以在一个规则集中引入其他规则集或混入集中的属性,通过一行
mixin()
代码实现多行属性的声明,在 CSS 中不支持这种语法,因此对于拥有大量相同的属性的规则集需要重复书写,造成代码的冗余和复杂。 - Less 支持嵌套书写,嵌套格式类似于 HTML,层级结构清晰,书写更方便,CSS 中所有的选择器都是选择级联的方式并列书写。
- Less 支持
+
、-
、*
、/
运算操作,CSS 中只能使用calc()
函数来实现运算。 - Less 增加了很多函数功能,可以通过调用函数快速实现属性值的变换等。
四. Vue 项目中 Less 的使用
1. 安装 less 依赖包
npm install -D less less-loader@latest
2. 在.vue
文件中的<style>
标签中添加lang="less"
<style lang="less" scoped></style>
3. 在.vue
文件中可以引入全局less
文件
import '@/assets/common.less'
4. 实现第三方组件库的样式穿透
4.1 原理
在
Vue
项目中,当我们引入第三方组件库时,需要在局部组件中修改第三方组件库样式的同时又不影响其他组件中该第三方组件的样式。但由于第三方组件通常会对其内部样式进行一定程度的封装,不易直接修改。这时候我们就需要样式穿透来达到修改第三方组件样式的目的。
举个栗子 🌰:
组件LessNotes.vue
文件中引用了两个组件:Demo1
、Demo2
<template>
<div>
<Demo1 />
<Demo2 />
</div>
</template>
<script>
import Demo1 from './components/Demo1'
import Demo2 from './components/Demo2'
export default {
name: 'LessNotes',
components: {
Demo1,
Demo2,
},
}
</script>
Demo1
和Demo2
的组件中均含有Ant Design Vue
组件库中的日期选择框<a-data-picker/>
组件,同时在两个组件的<style>
标签中均加入了scoped
属性,实现组件的私有化,表示当前样式标签中的所有声明只属于当前模块,不对全局造成样式污染。
<template>
<a-date-picker type="date" placeholder="开始日期" />
</template>
<script>
export default {
name: 'Demo1' / 'Demo2',
}
</script>
<style lang="less" scoped></style>
当组件编译为 html
文件后,由于加入了 scoped
属性实现了组件私有化,每个 Vue
组件都会生成一个格式为data-v-xxx
的唯一标识作为html
元素的属性,例如LessNotes.vue
组件生成的是:data-v-e014a03e
属性,Demo1.vue
组件生成data-v-1fafa096
属性, Demo2.vue
组件生成data-v-1fbdb817
属性。
这时候,如果需要修改 Demo1
组件中的<a-date-picker>
第三方组件的边框颜色为蓝色,不使用穿透的写法(修改无效)
<style lang="less" scoped>
.ant-calendar-picker-input.ant-input {
border-color: #216df5;
}
</style>
加入穿透的写法:
<style lang="less" scoped>
/deep/.ant-calendar-picker-input.ant-input {
border-color: #216df5;
}
</style>
Demo1
组件中边框的颜色变为了蓝色,而 Demo2
中的边框颜色依然为默认的灰色,即实现了局部修改第三方样式的效果
为什么加了/deep/
就可以实现了呢? 是/deep/
具有魔法吗?
首先看一下选择器.ant-calendar-picker-input.ant-input
在 html
中的格式:
当不进行第三方组件样式穿透时,编译成的真实的 css
选择器为:
.ant-calendar-picker-input.ant-input[data-v-1fafa096] {
}
也就是说我们设置的样式只会对含有data-v-1fafa096
属性的input
标签元素生效,而我们想要修改的input
标签中并没有该属性,所以失效啦!
加入穿透/deep/
声明之后,会将[data-v-1fafa096]
属性选择器写到该组件类选择器的前面,换句话说就是/deep/
实现了深度选择器,首先通过属性选择器匹配含有属性值为data-v-1fafa096
的元素,再在该元素的后代元素中匹配.ant-calendar-picker-input.ant-input
类,魔法就生效啦!
因此当前选择器的声明仅对标识符为data-v-1fafa096
的Vue
单文件组件中的.ant-calendar-picker-input.ant-input
类元素有效,也就实现了局部组件的样式修改,同时不污染全局样式。
另外,需要注意的是并不是所有的第三方组件库的组件都需要使用穿透才能进行样式修改
再举个栗子 🌰:
在 Demo1
组件中引入一个按钮 Button
组件
<template>
<div>
<a-button>点击按钮</a-button>
</div>
</template>
<script>
export default {
name: 'Demo1',
}
</script>
<style lang="less" scoped>
.ant-btn {
color: #ccc;
}
</style>
并没有进行样式穿透,但修改依然有效
这是因为第三方组件库的封装特性,使得html
中生成的button
元素已经自动加上了其所在Vue
组件的唯一标识属性[data-v-1fafa096]
, CSS
属性选择器不使用穿透也刚好能匹配到该html
元素,所以样式修改会生效。
所以什么时候使用穿透,就是要看生成的html
标签有没有加上其所在Vue
组件的唯一标识属性[data-v-xxx]
4.2 穿透的使用
- 在 vue2 中使用
/deep/
或::v-deep
选择器进行样式穿透:
<style lang="less" scoped>
/deep/ .ant-modal {
.ant-modal-body {
padding: 10px 0;
}
}
::v-deep .ant-modal {
.ant-modal-body {
padding: 10px 0;
}
}
</style>
- 在 vue3 中使用
:deep()
或::v-deep()
选择器进行样式穿透:
<style lang="less" scoped>
:deep(.ant-modal) {
.ant-modal-body {
padding: 10px 0;
}
}
::v-deep(.ant-modal) {
.ant-modal-body {
padding: 10px 0;
}
}
</style>
4.3 不同样式语法实现穿透的方式
- 对于
CSS
语言,可以使用>>>
、/deep/
、::v-deep
进行样式穿透。 - 对于
Less
语言,在 vue2 中可以使用/deep/
、::v-deep
进行样式穿透,在 Vue3 中应该采用:deep()
、::v-deep()
进行样式穿透。 - 对于
sass
和scss
语言,可以使用/deep/
、::v-deep
进行样式穿透。 - 对于
stylus
语言,可以使用>>>
、::v-deep
进行样式穿透。