主题
移动端适配与 rem(三)
本页对应 移动端适配与 rem,共 7 段视频,约 56 分钟。
学习目标
- 能根据设计稿尺寸与基准根字号,使用“设计稿值 ÷ 基准值”计算 rem,并将盒子的宽高正确换算为 rem 单位。
- 能安装并配置 VS Code 的 Easy LESS 插件,将
.less文件编译生成.css文件,并在 HTML 中正确引入编译后的 CSS。 - 能区分 Less 单行注释与多行注释,并验证编译后仅多行注释保留在 CSS 中。
- 能使用 Less 完成加减乘除运算,正确使用括号写法处理除法,并说明多单位参与运算时以第一个单位为准的规则。
- 能使用 Less 嵌套编写后代选择器,并使用
&表示当前元素编写 hover、伪元素等样式,确保编译后的选择器正确。 - 能在极速问诊综合案例中运用 rem 换算、Less 嵌套与变量统一颜色和尺寸,完成移动端页面样式适配并验证修改变量后全局生效。
知识点
先理解 Less 与编译流程
Less 是 CSS 预处理器,文件后缀是 .less。它扩充了 CSS 语言,让 CSS 具备一定的逻辑性、计算能力。为什么需要它?因为写适配时经常要把设计稿量到的 px 换算成 rem,例如设计稿量到宽 68px、基准 37.5,就要写 68 / 37.5rem,手算约 1.813rem;高度 29px 也要再算 29 / 37.5rem。每次写盒子、字体大小都手算很啰嗦。怎么想?把计算交给 Less,量多少就写多少表达式。怎么做?在 VS Code 安装 Easy LESS 插件,保存 .less 文件后自动生成对应 CSS 文件,网页引入生成的 CSS。注意:浏览器不识别 Less 代码,目前阶段网页要引入对应的 CSS 文件。调试时如果保存后不生成 CSS,转写稿提到可能是 VS Code 或插件版本冲突,可点插件齿轮切换旧版本并重启 VS Code;转写稿提到 1.7.3 可用、新版本可能生成不了。易错点:不要直接在 HTML 里引入 .less;先确认插件生效。

注释
为什么需要注释?代码多时帮助程序员记忆和说明,注释在页面中无效。怎么想?Less 有两种注释:单行 // 注释内容,快捷键 ctrl + /;块注释 /* 注释内容 */,快捷键 Shift + Alt + A。怎么做?直接写在 Less 中。易错点:单行注释编译到 CSS 时会被去掉;块注释 CSS 本身认识,会保留下来。转写稿调试时看到“单行都过去了,多行留下了”,原因就是 CSS 认识 /* */。单行注释不能折行,折行容易报错。
运算
为什么需要运算?适配时经常要写 68 / 37.5rem 这类表达式。怎么想?加、减、乘直接书写计算表达式;除法需要添加小括号或 .。怎么做?例如宽度可写 (68 / 37.5rem),高度写 (29 / 37.5rem),Less 编译时自动算出结果。转写稿演示:直接写 68 / 37.5,生成 CSS 中会得到计算结果;加减乘也可直接写。易错点:除法用点写法时编辑器语法层面可能标红报错,转写稿推荐用括号写法;表达式存在多个单位以第一个单位为准,例如单位不一致时结果取遇到的第一个单位,所以不要随意混写,建议养成统一单位书写习惯。

嵌套
为什么需要嵌套?以前写后代选择器要一层层写 .box div p,结构深时麻烦。怎么想?把后代选择器按 HTML 嵌套关系写进大括号,Less 自动生成后代选择器。怎么做?.box { div { p { ... } } },保存后生成 .box div p。作用就是快速生成后代选择器。易错点:用 & 表示当前选择器,不会生成后代选择器,通常配合伪类或伪元素使用,例如 &:hover、&::after。转写稿强调:写 a:hover 时若直接嵌套容易变成后代关系,用 & 才表示当前元素;& 在哪个大括号里就代表哪个当前元素。

提示:用 & 表示当前选择器,不会生成后代选择器,通常配合伪类或伪元素使用

变量
为什么需要变量?主题色、基准字号等数据经常重复使用,产品改颜色时一个个改很麻烦。怎么想?变量是容器,存储数据,方便使用和修改。怎么做?定义变量:@变量名: 数据;;使用变量:CSS属性:@变量名;。例如:
less
// 定义变量
@myColor: pink;
// 使用变量
.box {
color: @myColor;
}
a {
color: @myColor;
}易错点:变量名不能以数字开头;定义和使用都要带 @;改一处变量,多处引用会一起变。转写稿还演示把 37.5rem 存成变量,宽度、高度计算时直接引用,减少重复书写。
导入、导出与禁止导出
为什么需要导入?公共样式要复用。怎么做?语法:@import “文件路径”;,如果是 less 文件可以省略后缀:
less
@import './base.less';
@import './common';为什么需要导出?控制生成的 CSS 放哪里。怎么做?在 less 文件第一行添加 // out: 存储URL,文件夹名称后面添加 /:
less
// out: ./index.css
// out: ./css/禁止导出则在 less 文件第一行添加:// out: false。易错点:导出、禁止导出都要放第一行,格式和空格要严谨;路径写清楚当前目录或目标文件夹。

整体准备:先看效果和目录

做“极速问诊”这类移动端页面,为什么需要先准备目录和基础文件?因为页面要同时处理 rem 适配、图标字体、less 编译和图片资源,如果路径乱了,后面样式不生效很难排查。怎么想?先按笔记把 HTML、CSS、less、iconfont、assets 分开,页面只引入编译后的 CSS 和 flexible.js。怎么做?目录参考下图:

HTML 中先引入图标字体、页面样式,并在 body 后引入 flexible.js:
html
<link rel="stylesheet" href="./iconfont/iconfont.css">
<link rel="stylesheet" href="./css/index.css">
<body>
......
<script src="./js/flexible.js"></script>
</body>less 入口写输出目录和基础样式:
less
// out: ../css/
@import "./base";易错点:页面最终引入的是 ./css/index.css,不是 .less;// out: ../css/ 要写对空格和路径。按转写稿的调试经验,VS Code 安装 Easy LESS 后保存 less 应生成 CSS,如果生成不了,先检查插件版本,必要时换旧版本并重启编辑器。
rem 换算与 less 运算:尺寸不要直接写死 px
为什么需要换算?转写稿里量出盒子 68、29,如果直接写 68px,在不同手机上大小固定,适配效果差;设计稿以 375 为基准,基准根字号常按 37.5 处理,所以要把设计稿像素值除以基准值写成 rem。怎么想?less 支持运算,可以直接写“设计稿值 / @rootSize”,让 less 帮我们算。怎么做?笔记中统一写成 (15 / @rootSize)、(44 / @rootSize) 这种形式。易错点:less 除法推荐加括号,转写稿提到点写法可能报红;单位通常写一个即可,多个单位时以第一个单位为准,不要乱混。
头部布局:flex 做左右分布
为什么需要?头部有返回、标题、问诊记录三块,用 flex 最直接。怎么想?容器左右分布,用 justify-content: space-between,再给左右内边距和固定高度。怎么做:
html
<!-- 头部 -->
<header>1</header>less
// 头部
header {
display: flex;
justify-content: space-between;
padding: 0 (15 / @rootSize);
height: (44 / @rootSize);
background-color: pink;
line-height: (44 / @rootSize);
}易错点:高度和行高要一起换算;padding 的简写顺序不要写反。调试时先保留背景色,确认盒子位置,再换正式颜色。
头部内容:图标、标题、右侧链接
为什么需要?头部不只是布局,还要有返回图标和“问诊记录”入口。怎么想?左侧放 iconfont,中间 h3,右侧 a。怎么做:
html
<a href="#" class="back"><span class="iconfont icon-left"></span></a>
<h3>极速问诊</h3>
<a href="#" class="note">问诊记录</a>less
.icon-left {
font-size: (22 / @rootSize);
}
h3 {
font-size: (17 / @rootSize);
}
.note {
font-size: (15 / @rootSize);
color: #2CB5A5;
}易错点:iconfont 类名要和字体文件里的类名一致;字号也要 rem 化,不要漏掉换算。
banner 区域:图片与说明文字
为什么需要?banner 承担“20s 快速匹配专业医生”的视觉引导。怎么想?整体居中,图片固定换算宽高,下面放说明文字,重点数字用 span 变色。怎么做:
html
<!-- banner -->
<div class="banner">
<img src="./assets/entry.png" alt="">
<p><span>20s</span> 快速匹配专业医生</p>
</div>less
// banner
.banner {
margin-top: (30 / @rootSize);
margin-bottom: (34 / @rootSize);
text-align: center;
img {
margin-bottom: (18 / @rootSize);
width: (240 / @rootSize);
height: (206 / @rootSize);
}
p {
font-size: (16 / @rootSize);
span {
color: #16C2A3;
}
}
}易错点:图片路径和文件名要核对;text-align: center 只影响行内内容,图片本身仍要设置宽高。
问诊类型布局:li 卡片与 a 垂直居中
为什么需要?问诊类型是多条卡片列表,每条都要有边框、圆角、间距。怎么想?ul 里放 li,li 控制卡片外观,a 用 flex 让图片、文字、右箭头一行排列并垂直居中。怎么做:
html
<!-- 问诊类型 -->
<div class="type">
<ul>
<li>
<a href="#">
<div class="pic">1</div>
<div class="txt">2</div>
<span class="iconfont icon-right"></span>
</a>
</li>
<li>2</li>
</ul>
</div>less
// 问诊类型
.type {
padding: 0 (15 / @rootSize);
li {
margin-bottom: (15 / @rootSize);
padding: 0 (15 / @rootSize);
height: (78 / @rootSize);
border: 1px solid #EDEDED;
border-radius: (4 / @rootSize);
a {
display: flex;
align-items: center;
// 内容在78里面垂直居中
height: (78 / @rootSize);
}
}
}易错点:li 有高度后,a 也要给高度,否则 flex 垂直居中不明显;边框和圆角也要 rem 化。
问诊类型内容:flex:1 推右箭头
为什么需要?每条卡片里图片、标题说明、右箭头要稳定排列。怎么想?图片固定宽高,文字区 flex:1 占剩余空间,右箭头自然靠右。怎么做:
html
<div class="pic">
<img src="./assets/type01.png" alt="">
</div>
<div class="txt">
<h4>三甲图文问诊</h4>
<p>三甲主治及以上级别医生</p>
</div>
<span class="iconfont icon-right"></span>less
img {
margin-right: (14 / @rootSize);
width: (40 / @rootSize);
height: (40 / @rootSize);
}
.txt {
flex:1;
h4 {
font-size: (16 / @rootSize);
color: #3C3E42;
line-height: (24 / @rootSize);
}
p {
font-size: (13 / @rootSize);
color: #848484;
}
}
.iconfont {
font-size: (16 / @rootSize);
}易错点:flex:1 要写在 .txt 上,不是图片;嵌套写法要保证层级正确。转写稿还提醒:less 嵌套能自动生成后代选择器,遇到 hover 这类当前元素状态可用 &,避免把代码迁走时漏掉样式。
课件图示

代码示例
示例一:Less 核心语法(注释 / 运算 / 嵌套 / 变量)
index.less
less
// out: ./css/
// 单行注释:不会被编译进 CSS
/* 块注释:会被保留到生成的 CSS 中 */
// 定义变量
@myColor: pink;
@rootSize: 37.5rem;
.box {
width: (68 / @rootSize);
height: (29 / @rootSize);
background-color: @myColor;
.son {
width: (50 + 6px);
height: (60 - 10px);
font-size: (3 * 6px);
line-height: (29 / 2);
background-color: orange;
// & 表示当前选择器 .son
&:hover {
background-color: @myColor;
}
}
}生成的 css/index.css
css
/* 块注释:会被保留到生成的 CSS 中 */
.box {
width: 1.81333333rem;
height: 0.77333333rem;
background-color: pink;
}
.box .son {
width: 56px;
height: 50px;
font-size: 18px;
line-height: 14.5;
background-color: orange;
}
.box .son:hover {
background-color: pink;
}index.html
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Less 基础</title>
<!-- 页面引入的是 Less 生成的 CSS,不是 .less -->
<link rel="stylesheet" href="./css/index.css">
</head>
<body>
<div class="box">
<div class="son">子盒子</div>
</div>
</body>
</html>要点与易错点:
// out: ./css/必须写在文件第一行,冒号后要有空格,文件夹名后面加/;不加这句会默认把 CSS 生成到 less 文件同级目录。- 浏览器不认识
.less,HTML 里只能<link>到 Easy LESS 自动生成的.css文件;保存 less 后才会有新 CSS。 //单行注释不会进入 CSS;/* */块注释会原样保留到 CSS 里。- 加、减、乘直接写表达式;除法必须加括号
(68 / @rootSize)。另一种.写法(68 ./ 37.5rem)语法上能算,但 VS Code 会报红,不推荐。 - 表达式里存在多个单位时,以第一个单位为准,所以统一把单位写在第一个数上最省心。
- 嵌套会自动拼成后代选择器;
&代表当前选择器本身,不会再生成后代选择器,专门用于:hover、::after这类伪类/伪元素。
示例二:导入(@import)与导出 / 禁止导出
base.less(公共样式文件,第一行禁止导出)
less
// out: false
@rootSize: 37.5rem;
@themeColor: #2CB5A5;index.less(导入公共文件 + 导出到上级 css 目录)
less
// out: ../css/
@import "./base";
.header {
height: (44 / @rootSize);
line-height: (44 / @rootSize);
background-color: @themeColor;
.note {
font-size: (15 / @rootSize);
color: @themeColor;
}
}index.html
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Less 导入导出</title>
<link rel="stylesheet" href="./css/index.css">
</head>
<body>
<div class="header">
<a href="#" class="note">问诊记录</a>
</div>
</body>
</html>要点与易错点:
@import "./base";导入 less 文件时可以省略后缀,写./base或./base.less都行。- 被导入的
base.less第一行写// out: false就不再单独生成 base.css,否则会多出一个无用的 CSS 文件。 - 变量
@rootSize定义在base.less里,index.less导入后即可直接使用;如果没导入就使用会编译报错。 - 变量名不能以数字开头(
@37.5是错的,@rootSize是对的)。 // out: ../css/用相对路径指定导出位置,注意别和// out: ./css/(当前目录下的 css 文件夹)搞混。
示例三:综合案例——极速问诊(头部 + banner + 问诊类型)
base.less
less
// out: false
@rootSize: 37.5rem;index.less
less
// out: ../css/
@import "./base";
// 头部
header {
display: flex;
justify-content: space-between;
padding: 0 (15 / @rootSize);
height: (44 / @rootSize);
background-color: pink;
line-height: (44 / @rootSize);
}
.icon-left {
font-size: (22 / @rootSize);
}
h3 {
font-size: (17 / @rootSize);
}
.note {
font-size: (15 / @rootSize);
color: #2CB5A5;
}
// banner
.banner {
margin-top: (30 / @rootSize);
margin-bottom: (34 / @rootSize);
text-align: center;
img {
margin-bottom: (18 / @rootSize);
width: (240 / @rootSize);
height: (206 / @rootSize);
}
p {
font-size: (16 / @rootSize);
span {
color: #16C2A3;
}
}
}
// 问诊类型
.type {
padding: 0 (15 / @rootSize);
li {
margin-bottom: (15 / @rootSize);
padding: 0 (15 / @rootSize);
height: (78 / @rootSize);
border: 1px solid #EDEDED;
border-radius: (4 / @rootSize);
a {
display: flex;
align-items: center;
// 内容在 78 里面垂直居中
height: (78 / @rootSize);
img {
margin-right: (14 / @rootSize);
width: (40 / @rootSize);
height: (40 / @rootSize);
}
.txt {
flex: 1;
h4 {
font-size: (16 / @rootSize);
color: #3C3E42;
line-height: (24 / @rootSize);
}
p {
font-size: (13 / @rootSize);
color: #848484;
}
}
.iconfont {
font-size: (16 / @rootSize);
}
}
}
}index.html
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>极速问诊</title>
<link rel="stylesheet" href="./iconfont/iconfont.css">
<link rel="stylesheet" href="./css/index.css">
</head>
<body>
<!-- 头部 -->
<header>
<a href="#" class="back"><span class="iconfont icon-left"></span></a>
<h3>极速问诊</h3>
<a href="#" class="note">问诊记录</a>
</header>
<!-- banner -->
<div class="banner">
<img src="./assets/entry.png" alt="">
<p><span>20s</span> 快速匹配专业医生</p>
</div>
<!-- 问诊类型 -->
<div class="type">
<ul>
<li>
<a href="#">
<div class="pic"><img src="./assets/type01.png" alt=""></div>
<div class="txt">
<h4>三甲图文问诊</h4>
<p>三甲主治及以上级别医生</p>
</div>
<span class="iconfont icon-right"></span>
</a>
</li>
</ul>
</div>
<script src="./js/flexible.js"></script>
</body>
</html>要点与易错点:
- 设计稿量出来的都是 px,写进 less 时全部换成
(数值 / @rootSize),并且括号不能漏,漏了就是非法除法。 header用display: flex; justify-content: space-between;让左返回、中标题、右「问诊记录」自动分开;line-height与height同值实现垂直居中。li有高度78,里面的a也要再写一次height: (78 / @rootSize),配合align-items: center才能让内容真正垂直居中。.txt用flex: 1占满剩余空间,才能把右侧.iconfont箭头挤到最右边。img、.txt、.iconfont都是a的子元素,所以直接嵌在a { }里写,Less 会自动拼成.type li a img这样的后代选择器。- 案例依赖
flexible.js动态设置根字号、iconfont字体图标和assets里的图片,跑起来前要先准备好这三样,否则页面只有色块和文字。
常见错误
1. HTML 里直接 <link rel="stylesheet" href="./css/index.less">,页面样式全丢
- 现象:浏览器控制台无报错,但样式一条都没生效。
- 原因:浏览器只认 CSS,
.less文件被当作未知文本处理;且此时 Easy LESS 可能根本没生成对应 css。 - 修法:引入 Easy LESS 生成的
.css文件;确认 less 文件已保存、同目录(或指定目录)下确实出现了同名 css 再引入。
2. 除法写完保存后报红 / 值没被计算
- 现象:
width: 68 / 37.5rem;编辑器标红,编译后属性值原样输出或直接失效。 - 原因:Less 里
/在部分场景被当作分隔符而非除号,编辑器语法检查也认为格式非法。 - 修法:用括号把整个表达式包起来 ——
width: (68 / 37.5rem);,这是推荐写法,既不被报红又能正确计算;不推荐用.的写法(68 ./ 37.5rem)。
3. 伪类/伪元素写成后代选择器,hover 样式不生效
- 现象:
.link { :hover { color: red } }编译成.link :hover,鼠标移上去毫无反应。 - 原因:嵌套里不写
&会自动拼接成后代选择器,中间多了空格。 - 修法:
&:hover、&::after;注意&后面不能加空格,&只代表当前选择器本身,不会生成后代。
4. 保存 less 后死活不生成 css
- 现象:插件显示已安装,改完保存旁边就是不出现 css 文件。
- 原因:VS Code 版本与 Easy LESS 插件版本冲突(新版尤其常见)。
- 修法:在插件详情页点齿轮 → 安装其他版本,退回旧版(如 1.7.3),装完重启 VS Code 再试。
5. 导出指令写了却没生效,css 生成到了奇怪的位置
- 现象:
//out: ./css/写了,css 还是躺在 less 同目录;或干脆没反应。 - 原因:
//后少了空格(该指令对格式很严格)、目录路径末尾没加/、或者写成了/* out: ... */块注释。 - 修法:严格写
// out: ./css/,必须在文件第一行,目录结尾带/,只能使用单行注释。
6. 公共样式文件被单独编译出一份 css
- 现象:
base.less、common.less各自多出一个 css 文件,页面里还容易重复引入。 - 原因:Easy LESS 对每个 less 文件都会生成 css,导入文件也不例外。
- 修法:在只做被导入用途的文件第一行加
// out: false禁止导出;页面只引入最终那一个 css。
7. 适配尺寸全乱,元素忽大忽小
- 现象:同一段
(68 / 37.5rem)在大小屏上比例不对,或某处尺寸明显异常。 - 原因:单位没统一——表达式里混了 px 和 rem,Less 以第一个出现的单位为准,后面的单位被丢弃。
- 修法:全程统一 rem,单位习惯性写在第一个数上,不要一处写 px 一处写 rem。
练习
练习 1|环境自查 新建 test.less,写入 .box { width: (100 / 37.5rem); } 并保存。检查是否在同目录生成 test.css、里面数值是否为 2.66666667rem。若未生成,自行排查插件版本并记录你最终使用的版本号。
练习 2|注释与导出 新建 base.less 和 index.less:
base.less中定义变量与公共样式,并让它不产出 css;index.less中导入它,并把 css 导出到./css/目录。 完成后说明:单行注释与块注释在生成的 css 中分别留下了什么。
练习 3|计算改造 把下面这段"手算版"改写成 Less 运算版(基准值 37.5,统一用变量):
css
.box { width: 1.813rem; height: 0.773rem; font-size: 0.373rem; }要求:出现除法的地方用括号写法;改完后把基准值一次性换成 75 再编译,观察三个值的变化。
练习 4|嵌套重构 把这段 CSS 改写成嵌套写法,并保证编译结果完全一致:
css
.card { padding: 10px; }
.card .title { font-size: 16px; }
.card .title a { color: #333; }
.card .title a:hover { color: #16c2a3; }
.card::after { content: ""; }要求:a:hover 与 ::after 必须用 & 实现,不得出现重复写选择器的情况。
练习 5|案例复用 用极速问诊的头部结构(header + .back + h3 + .note),完成:
- 高度 44、左右内边距 15、字号分别 22/17/15,全部走 rem 换算;
- 主题色
#2CB5A5抽成变量; - 给
.note加 hover 变色(用&)。 最后把主题色变量改成另一个颜色,验证只改一处即全站生效。
原视频对照
| # | 视频 | 时长 |
|---|---|---|
| 1 | rem计算 | 10:31 |
| 2 | less体验 | 05:27 |
| 3 | less介绍 | 07:10 |
| 4 | less注释 | 05:30 |
| 5 | less运算 | 09:03 |
| 6 | less嵌套 | 08:48 |
| 7 | less变量 | 10:14 |
小结
- Less 的定位是"写起来省事的 CSS 生产工具",产物永远是 css,交付物和引入对象始终是 css,别搞混。
- 整套工作流只有三步:装 Easy LESS → 写
.less并保存 → 页面引入生成的.css;任何一环断了,症状都是"样式没反应",按这三步倒查最快。 - 运算的核心价值不是"能算",而是"设计稿量多少就写多少",把换算这一步交给工具,人只负责抄数值。
- 除法是唯一需要特殊写法的运算符,统一用括号包起来,既避开报红也避开歧义,形成肌肉记忆即可。
- 单位混用是适配类 bug 的头号来源,Less 只认第一个单位,所以团队内必须约定统一写法。
- 嵌套解决的是"选择器重复书写",
&解决的是"当前元素自身状态",两者用途不同,混淆就会多出空格导致样式失效。 - 变量的收益在"改"上而不在"写"上:主题色、基准字号、圆角这类高频复用值才值得抽,随手什么都抽反而降低可读性。
- 导入 + 禁止导出 + 统一导出目录,是把零散 less 组织成可维护工程的最小组合,规模一大就必须用。