面对一张 960px 宽的设计稿,主内容区 600px,侧边栏 300px,你心算了一下------62.5% 和 31.25%,然后把这几个百分比硬编码进了 CSS。
三个月后,设计稿宽度调整为 1200px。你不得不翻出计算器,把所有比例重新算一遍。
于是你想到 Sass 的数学运算,把设计稿的像素值直接写进代码,让机器自动算出百分比。但 CSS 也有 calc(),它不也能在样式里写表达式吗?calc(600px / 960px * 100%) 不就等于 62.5% 吗?为什么还需要 Sass?
问题的关键,在于计算到底发生在哪一层。本文将深入解析 Sass 运算符的用法与变革,并对比 CSS calc(),彻底讲清"编译时计算"与"运行时计算"的本质区别,帮你做出正确的技术选择。

一、Sass 支持的数学运算符
Sass 提供了一套标准运算符,让你可以像写程序一样在样式表中做计算:
| 运算符 | 含义 | 示例 |
|---|---|---|
+ |
加法 | width: 100px + 20px; → 120px |
- |
减法 | width: 100px - 20px; → 80px |
* |
乘法 | width: 100px * 2; → 200px |
math.div() |
除法 | math.div(100px, 2) → 50px |
% |
取模 | 10 % 3 → 1 |
⚠️ 注意 :除法是 math.div(),不是 /。这恰恰是"编译时计算"与 CSS 原生语法划清界限的关键一步。
二、除法为什么变成了 math.div()?
在 CSS 中,/ 是合法的属性值分隔符。例如:
css
font: 12px/1.5 Helvetica; /* 字号12px,行高1.5 */
如果 Sass 继续把 / 当作除法运算符,编译器将无法区分你的意图------这到底是 CSS 的字体简写,还是一次数学运算?
这种歧义正是"编译时层"与"标准层"的冲突。Sass 在编译时要对源码做运算,而原生 CSS 允许 / 作为字面量存在。为了把两个层级彻底解耦,Sass 正式废弃了 / 除法,要求使用 math.div() 函数:
scss
@use "sass:math"; // 必须引入内置模块
// ❌ 旧式写法(已弃用)
width: (600px / 960px) * 100%;
// ✅ 新式标准写法
width: math.div(600px, 960px) * 100%;
这一变化让代码意图一目了然:凡是看到 math.div(),你就知道这是在 Sass 层做除法,而不是把有歧义的表达式留给浏览器。
三、实例剖析:构建基于 960px 的流式网格
来看一个 Sass 官网的流体网格示例:
scss
@use "sass:math";
.container {
display: flex;
}
article[role="main"] {
width: math.div(600px, 960px) * 100%;
}
aside[role="complementary"] {
width: math.div(300px, 960px) * 100%;
margin-left: auto;
}
编译后的 CSS
css
.container {
display: flex;
}
article[role=main] {
width: 62.5%;
}
aside[role=complementary] {
width: 31.25%;
margin-left: auto;
}
背后发生了什么?
整个过程分两步:
- 像素相除,单位抵消
math.div(600px, 960px)→ 600 ÷ 960 = 0.625(无单位纯数字) - 数字乘以百分比
0.625 * 100%→62.5%
同样,300 ÷ 960 = 0.3125,乘以 100% 得到 31.25%。
最终 CSS 中没有一丁点 Sass 运算的痕迹,只有浏览器可以直接使用的静态百分比值。这说明:计算已经在编译层完成了,浏览器根本感知不到曾经有过任何数学表达式。
这带来了什么好处?
- 设计稿直译 :设计师标注"总宽 960px,主内容 600px",你直接写
math.div(600px, 960px),不用动脑换算。 - 一改全改 :如果总宽从 960 变成 1200,只需把源码中的
960px替换为1200px,所有比例自动重算,告别手动维护。
四、运算中的单位处理规则
Sass 对单位的处理非常"物理",理解这些规则可以避免踩坑。
| 运算场景 | 规则 | 示例 | 结果 |
|---|---|---|---|
| 相同单位相除 | 单位抵消,得纯数字 | math.div(10px, 5px) |
2 |
| 不同单位相加 | 不允许,直接报错 | 10px + 5em |
❌ 编译错误 |
| 数字乘带单位值 | 单位保留 | 2 * 10px |
20px |
| 数字乘百分比 | 得百分比 | 0.5 * 100% |
50% |
| 百分比除数字 | 百分比保留 | math.div(100%, 2) |
50% |
五、Sass 运算 vs CSS calc():计算该放在哪一层?
现在回到核心问题:为什么不用 calc()?它不也能算出 62.5% 吗?
calc() 确实能做同样的数学运算,但它和 Sass 运算处在完全不同的层。
| 对比维度 | Sass 数学运算 | CSS calc() |
|---|---|---|
| 计算时机 | 编译时(开发阶段) | 运行时(浏览器渲染) |
| 输出结果 | 静态固定值(如 62.5%) | 表达式保留,浏览器实时计算 |
| 动态性 | ❌ 只能基于源码中的已知值 | ✅ 可混用不同单位、CSS 变量 |
| 性能开销 | 零 | 微小,但复杂度高时可能累积 |
| 适用场景 | 设计标准固定、预计算 | 动态混合视口、容器查询等 |
5.1 什么时候用 Sass 运算?
当计算依据在设计阶段就确定不变时,用 Sass 在编译期算好,输出静态值:
scss
// 总宽固定 960,永远不会变 → Sass 运算
width: math.div(600px, 960px) * 100%; // 输出 62.5%
浏览器收到的就是 62.5%,没有任何表达式需要解析。
5.2 什么时候用 calc()?
当计算依赖运行时才能确定的值时,用 calc() 交给浏览器:
css
/* 依赖视口宽度,只有浏览器知道 → calc() */
width: calc(100vw - 2rem);
/* 依赖 CSS 变量,运行时变化 → calc() */
width: calc(var(--content-width) * 0.6);
5.3 两者互补
- 值在设计阶段已知 → Sass 运算
- 值在运行时才确定 → CSS calc()
二者不是替代关系,而是互补。事实上,你还可以在 Sass 里写 calc(),将编译时值与运行时值混合使用,比如:
scss
.element {
width: calc(100% - #{$sidebar-width});
}
这样,$sidebar-width 在编译时被替换为具体值,而 100% 由浏览器动态计算。你在 Sass 层处理静态部分,把动态部分留给浏览器------各司其职,边界清晰。
六、其他运算符的实际场景
官网示例聚焦于乘法和除法,但其他运算符同样高频。
6.1 + 和 -:动态计算间距
scss
$spacing-base: 8px;
$spacing-lg: $spacing-base + 8px;
$spacing-sm: $spacing-base - 2px;
.card {
padding: $spacing-lg $spacing-base;
margin-bottom: $spacing-base * 3;
}
6.2 % 取模:循环中判断奇偶
scss
@use "sass:math";
@for $i from 1 through 6 {
.col-#{$i} {
width: math.div(100%, 6) * $i;
@if $i % 2 == 0 {
background: #f0f0f0; // 偶数列加背景
}
}
}
6.3 封装成函数:让复用更优雅
scss
@use "sass:math";
// 将比例计算封装为函数
@function col-width($col, $total: 960px) {
@return math.div($col, $total) * 100%;
}
.main { width: col-width(600px); } // 62.5%
.side { width: col-width(300px); } // 31.25%
七、总而言之
Sass 的运算符体系,本质上是在做一件事:把设计阶段已知的数值计算,提前到编译时完成,最终输出纯净、无冗余、高性能的静态 CSS。
当我们面对"用 Sass 算还是用 calc() 算"的疑问时,本质上是在问:这个计算依赖的是静态设计稿,还是动态的浏览器环境?
- 如果答案在设计稿上------请把它放在 Sass 编译层,用
math.div()等运算符完成预计算,还浏览器一个干净的静态值。 - 如果答案要到运行时才能揭晓------请使用 CSS
calc(),让它待在浏览器层,动态响应变化。
掌握这个分层思维,你就能在正确的层级做正确的计算,让源码保持直观可维护,让输出保持高性能零开销。