前言
石匠敲击石头的第 9 次
我们都知道在 CSS 中有很多的单位,但在我平常开发中用的最多的却只有 px 和 %,其它的没怎么用到过。
想了一下没用到的原因可能是业务需求对响应式要求不高,或者是对其它单位不够熟悉,所以打算写一篇文章来好好梳理一下,如果哪里写的有问题欢迎指出。
CSS 单位分类
虽然 CSS 中的单位有很多,但是可以分为以下两大类。
固定单位
CSS 支持多种固定单位,我们平常开发中最常用的就是 px(像素) ,不常用的单位还有:mm(毫米)、cm(厘米)、in(英寸)、pt(点,印刷术语)、pc(派卡,印刷术语)。
它们之间的换算关系: 1in = 25.4mm = 2.54cm = 6pc = 72pt = 96px
固定单位的值是固定的,不随其他因素(如父元素、视口、字体大小)变化,适合精确控制尺寸,但在响应式设计中不够灵活。
相对单位
这类单位的值不是固定的,是基于某个参照基准(如父元素、视口、字体大小等)来计算实际的值,非常适合用于响应式设计,接下来我将详细讲这类单位。
em 单位
em 单位基于当前元素的字号 font-size 来计算具体的值。
            
            
              css
              
              
            
          
          .box {
  font-size: 18px;
  padding: 1em;
}
        上述代码中 padding 属性实际的值为 18px,计算过程为 1 x 18px = 18px。
当前元素的字号 font-size 没有指定时,em 单位的属性值如何计算
当前元素会先通过继承 获取父元素的 font-size 值作为自己的字号,再基于自己的字号计算其它 em 单位的属性值,例如下面的例子。
            
            
              css
              
              
            
          
          body {
  font-size: 16px;
}
.box {
  padding: 1em;
}
        上述代码中 padding 属性值的计算过程如下:
- 首先继承 
<body>元素的font-size属性值作为当前元素的字号,如果对继承不太熟悉可以看我之前写过的这篇文章。 - 然后再用当前元素的字号(继承的值为 
16px)计算padding属性的值,计算过程为16px x 1 = 16px。 
当前元素的字号 font-size 使用 em 单位时,其它 em 单位的属性值如何计算
当前元素会先通过继承 获取父元素的 font-size 作为基准值来计算自己的字号,再基于自己的字号计算其它 em 单位的属性值,例如下面的例子。
            
            
              css
              
              
            
          
          body {
  font-size: 16px;
}
.box {
  font-size: 0.8em;
  padding: 1em;
}
        上述代码中 padding 属性值的计算过程如下:
- 首先计算当前元素 
font-size属性的值,先继承<body>元素的font-size属性值,然后再与0.8相乘,16px x 0.8 = 12.8px。 - 然后再用当前元素的字号(
12.8px)计算padding属性的值,计算过程为12.8px x 1 = 12.8px。 
em 换算公式
想要的像素大小 / 当前元素的字号大小 = em 的值。
例如:当前元素的字号大小为 16px,想设置一个 8px 的内边距,但是又想内边距大小会随着当前元素的字号大小的变化而变化,此时可以指定内边距的值为 0.5em,计算过程为:8px / 16px = 0.5em。
em 指定多重嵌套元素的字号时会出现字体逐渐缩小或放大的问题
例如下面这段代码。
            
            
              css
              
              
            
          
          body {
  font-size: 16px;
}
ul {
  font-size: 0.8em;
}
        
            
            
              html
              
              
            
          
          <ul>
  <li>
    Top level
    <ul>
      <li>
        Second level
        <ul>
          <li>
            Third level
            <ul>
              <li>
                Fourth level
                <ul>
                  <li>Fifth level</li>
                </ul>
              </li>
            </ul>
          </li>
        </ul>
      </li>
    </ul>
  </li>
</ul>
        
原因: <ul> 会继承父级元素的 font-size 属性的值进行计算,导致越继承字号越小。
计算过程:
- Top level 层,字号为 
12.8px,<li>继承<ul>的font-size属性值,<ul>的font-size属性值继承<body>的字号进行计算,结果为16px x 0.8 = 12.8px - Second level 层,字号为 
10.24px,<li>继承<ul>的font-size属性值,<ul>的font-size属性值继承 Top level 层<ul>的字号进行计算,结果为12.8px x 0.8 = 10.24px - Third level 层,字号为 
8.192px,<li>继承<ul>的font-size属性值,<ul>的font-size属性值继承 Second level 层<ul>的字号进行计算,结果为10.24px x 0.8 = 8.192px 
以此类推计算。
该问题的解决方案:
            
            
              css
              
              
            
          
          body {
  font-size: 16px;
}
ul {
  font-size: 0.8em;
}
/* 除第一个ul之外的ul都默认继承父级的字号,1em不会缩小和放大字号 */
ul ul {
  font-size: 1em;
}
        
⚠️ 注意: 这样的解决方案通过提升选择器的优先级覆盖规则,依旧不好,也可以使用 rem 单位来解决这个问题。
            
            
              css
              
              
            
          
          :root {
  font-size: 16px;
}
ul {
  font-size: 0.8rem;
}
        rem 单位
rem 单位是 root em 的缩写,与 em 单位类似,但比 em 单位的使用简单多了,该单位始终基于根节点的字号 font-size 来计算具体的值。
使用前先使用 html 或者 :root 来为 <html> 根节点指定默认字号,当然也可以不指定,在大多数浏览器中默认为 16px。
            
            
              css
              
              
            
          
          /* 等价于使用html {},但是:root选择器优先级更高(0,1,0) */
:root {
  font-size: 1em;
}
ul {
  font-size: 0.8rem;
}
        上述代码计算过程:
<html>根元素的字号为16px,根节点上的em是相对于浏览器的默认字号进行计算,默认字号通常是16px,计算过程为16px x 1 = 16px<ul>元素的字号为12.8px,基于根节点的字号进行计算,计算过程为16px x 0.8 = 12.8px
% 单位
% 单位的计算规则如下:
- 当元素的 
width属性使用%单位时,是基于元素包含块的width值进行计算 - 当元素的 
height属性使用%单位时,是基于元素包含块的height值进行计算 - 当元素的 
margin或padding属性使用%单位时,不论是垂直方向还是水平方向,都是基于元素包含块的width值进行计算 
⚠️ 注意: border-radius、translate、background-size 属性使用 % 单位时是不基于包含块,是基于自身的大小。
包含块: 是一个元素在计算尺寸(如宽度、高度、边距、定位时)所参考的矩形区域。
初始包含块: 如果一个元素没有明确的包含块(如:<html> 元素和当元素使用了 position: fixed 或 position: absolute,并且没有定位祖先元素),则它的包含块为初始包含块,初始包含块的大小通常等于浏览器视口的大小。
包含块的确定规则:
包含块的确定取决于元素的 position 属性。
- 静态定位 
position: static和相对定位position: relative- 包含块是最近的块级祖先元素的内容区域
 - 如果没有块级祖先元素,则包含块为初始包含块
 
 - 绝对定位 
position: absolute- 包含块是最近的 定位祖先元素(即 
position为relative、absolute、fixed或sticky的元素) 的 padding box(内容区域 + 内边距) - 如果没有定位祖先元素,则包含块是初始包含块
 
 - 包含块是最近的 定位祖先元素(即 
 - 固定定位 
position: fixed- 包含块是初始包含块
 
 - 粘性定位 
position: sticky- 包含块是最近的 滚动祖先元素(即 
overflow: auto或overflow: scroll的元素) 的内容区域 - 如果没有滚动祖先元素,则包含块是初始包含块
 
 - 包含块是最近的 滚动祖先元素(即 
 
vw 单位
vw 单位的全称是 viewport width,表示视口宽度,该单位始终基于浏览器视口宽度来计算具体的值,计算规则如下:
1vw等价于视口宽度的1%,例如视口宽度为750px,那么1vw = 750px x 1% = 7.5px100vw等价于视口宽度的100%,例如视口宽度为750px,那么100vw = 750px x 100% = 750px
vh 单位
vh 单位的全称是 viewport height,表示视口高度,该单位始终基于浏览器视口高度来计算具体的值,计算规则如下:
1vh等价于视口高度的1%,例如视口高度为600px,那么1vh = 600px x 1% = 6px100vh等价于视口高度的100%,例如视口高度为600px,那么100vh = 600px x 100% = 600px
vmin 单位
vmin 单位始终基于浏览器视口的最小边来计算具体的值,计算规则如下:
1vmin等价于视口最小边长度的1%,例如:- 如果视口尺寸为 
1200px × 600px(宽 > 高),则1vmin = 600px × 1% = 6px - 如果视口尺寸为 
500px × 800px(宽 < 高),则1vmin = 500px × 1% = 5px 
- 如果视口尺寸为 
 100vmin等价于视口最小边长度的100%,例如:- 如果视口尺寸为 
1200px × 600px(宽 > 高),则100vmin = 600px × 100% = 600px - 如果视口尺寸为 
500px × 800px(宽 < 高),则100vmin = 500px × 100% = 500px 
- 如果视口尺寸为 
 
vmax 单位
vmax 单位始终基于浏览器视口的最大边来计算具体的值,计算规则如下:
1vmax等价于视口最大边长度的1%,例如:- 如果视口尺寸为 
1200px × 600px(宽 > 高),则1vmax = 1200px × 1% = 12px - 如果视口尺寸为 
500px × 800px(宽 < 高),则1vmax = 800px × 1% = 8px 
- 如果视口尺寸为 
 100vmax等价于视口最大边长度的100%,例如:- 如果视口尺寸为 
1200px × 600px(宽 > 高),则100vmax = 1200px × 100% = 1200px - 如果视口尺寸为 
500px × 800px(宽 < 高),则100vmax = 800px × 100% = 800px 
- 如果视口尺寸为 
 
实践建议
- 
用
rem单位来设置字号(font-size)假设你根据设计稿开发一个网页,使用的都是
px单位,等你开发完成后交付给客户或者老板,得到反馈希望页面整体的字号希望再大一点,这时你需要手动调整所有px单位的值,非常的麻烦。但使用
rem单位来设置元素的字号就没有这个问题,只需要调整根元素的字号就可以调整页面整体的字号,可维护性非常高。⚠️ 注意: 使用
rem单位时不要设置根元素的字号为固定值,例如:css:root { /* 不推荐这样写 */ font-size: 16px; }因为使用固定值导致用户修改浏览器的默认字号不会影响已经使用
px单位或其它绝对单位的字号 ,所以不会修改根元素的默认字号,进而导致了使用rem单位的字号大小不会发生变化。最好使用如下的写法:
css:root { /* 也可以省略不写,直接使用浏览器的默认字号 */ font-size: 1em; }上述代码中根节点的字号大部分默认情况下是
16px,不使用固定值,便于用户可以修改浏览器的默认字号,修改默认字号对部分用户(存在视力障碍)很有需要。 - 
用
px单位来设置边框(border)边框的大小基本上在所有屏幕尺寸上都是固定的,所以使用固定单位
px即可。 - 
用
em单位来设置除字号、边框外其它大部分属性这块其它大部分属性主要指
border-radius、padding,margin等,让内边距或者外边距基于当前元素的字号大小来动态变化,确保与字号比例协调。 
总结
- CSS单位分类
- 固定单位(如 
px):值固定,适合精确控制 - 相对单位(如 
em、rem):基于参照基准动态计算,适合响应式设计 
 - 固定单位(如 
 - 常用单位特性
em:基于当前元素的font-size计算,易受嵌套影响rem:基于根元素font-size计算,避免嵌套问题%:基于包含块尺寸计算,常用于布局vw/vh:基于视口宽/高,vmin/vmax基于视口最小/最大边
 - 实践建议
- 字号用 
rem单位:便于全局调整,提升可维护性 - 边框用 
px单位:固定尺寸更稳定 - 除字号、边框外其它大部分属性用 
em单位:与字号联动,保持比例协调 - 避免根元素固定字号:保留用户自定义能力
 
 - 字号用