先记住这个答案
var() 函数只接收两个参数:第一个是自定义属性名,第二个是可选的备用值。从第一个逗号到结尾的所有内容都被当作一个备用值,不会拆成多个候选项。写 var(--x, red, blue) 时,备用值是 red, blue 这个整体字符串;如果 --x 未定义而且 red, blue 对当前属性不合法,整个声明变成无效,属性按 unset 处理。
var()只接受一个备用值- 多个逗号值合并为一个整体
- 变量已定义但值非法不触发回退
- 多级回退用嵌套
var()
var() 语法只允许一个备用值
规范里 var() 的形式是 var( <custom-property-name> , <declaration-value>? ),最多两个参数。第二个参数虽然允许包含逗号,但逗号不会起到"分隔多个候选"的作用。比如 background: var(--bg, linear-gradient(red, blue)) 里的备用值是完整的 linear-gradient(red, blue) 渐变,逗号只是渐变语法的一部分。
:root {
/* --accent 未定义 */
}
.box {
/* 备用值是整个 "red, blue",不是两个候选 */
color: var(--accent, red, blue);
/* "red, blue" 对 color 来说不合法 */
/* 最终 color 按 unset 处理,可能继承父级颜色 */
}
.card {
/* 正确的多级回退:嵌套 var() */
background: var(--card-bg, var(--theme-bg, #f5f5f5));
}.box 的 color 不会变红也不会变蓝,因为 red, blue 整体不是合法的颜色值。而 .card 用嵌套 var() 实现了三级回退:先看 --card-bg,没有就看 --theme-bg,都没有才用 #f5f5f5。
正确的多级回退要嵌套 var()
真正的多级回退只能通过嵌套来做。每一层 var() 的备用值本身又是一个 var() 表达式,内层先解析,解析不了再往下一层走。写成 var(--a, var(--b, var(--c, fallback))) 就能实现三级回退。
.button {
/* 优先用组件级变量 --btn-bg */
/* 没有就用主题级变量 --theme-primary */
/* 都没有就用硬编码兜底 */
background: var(--btn-bg, var(--theme-primary, #1976d2));
}开发者有时会错写成 var(--btn-bg, var(--theme-primary), #1976d2)。这时如果 --btn-bg 未定义,整个备用值变成 var(--theme-primary), #1976d2。浏览器把它替换进 background 后,内部的 var(--theme-primary) 会被求值,但最终拼出来的值格式可能不合法,导致声明无效。把 #1976d2 放进最内层 var() 的备用位才是正确写法。
已定义但值非法时不触发备用
有一个容易踩的坑:备用值只在自定义属性 guaranteed-invalid 时才启用。所谓 guaranteed-invalid,指的是变量未声明、显式设为 initial(且未用 @property 注册),或者存在循环依赖。
如果变量已经定义了,但值对目标属性不合法——比如 --x: 20px 然后写 color: var(--x, red)——浏览器不会用 red。变量替换发生在属性解析之前,替换后得到 color: 20px,这时才发现不合法,但已经来不及回退了,color 直接按 unset 处理。这个行为叫"无效值替换"(invalid at computed-value time)。
另外要注意 CSS 全局关键字:如果 --x 设成 inherit 或 revert,它们可能解析出有效值,同样不会触发备用。所以"变量存在就不用备用"和"变量有效才不用备用"是两回事,var() 走的是前一种逻辑。
容易答错的地方
- 以为
var(--a, red, blue)会依次尝试每个颜色 - 不会。逗号后面的所有内容是一个备用值
red, blue,不会拆开。如果属性只接受单个颜色值,这个备用值就不合法,属性变成unset,既不是红也不是蓝。 - 把变量非法值和未定义混为一谈
- 变量未定义时备用值生效;变量已定义但值对属性不合法时,备用值不生效,属性直接走
unset。这两种情况的结果完全不同,面试时需要分开讲。
面试官还会怎么问?
除了嵌套 var(),还有其他方式给变量设默认值吗?
可以用 @property 注册自定义属性并指定 initial-value。这样变量未定义时自动用初始值,不需要在每个使用处写备用。但 @property 只能为单个变量提供一层默认值,不能替代嵌套 var() 的多级回退。
var() 的备用值可以包含哪些字符?
几乎任意字符都行,包括逗号和空格,但不能有未闭合的括号、分号和感叹号。也可以嵌套另一个 var()。逗号虽然能写,但它属于同一个备用值的一部分,不会分出新参数。
自定义属性未定义而且没写备用值会怎样?
var(--x) 在 --x 未定义时解析为 guaranteed-invalid,整条声明变成无效,属性按 unset 处理。如果属性是可继承的就继承父级的值,否则用初始值。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。