map-merge 不支持深度合并,仅做单层覆盖;需手写递归函数 map-deep-merge 判断类型并递归处理嵌套 map;更推荐分层定义变量+显式注入,避免时机与作用域问题。

map-merge 本身不提供深度合并功能,Sass 原生 sass:map 模块(Dart Sass 1.40+)也**没有 map-deep-merge 函数**。所谓“深度合并”,必须自己实现或借助外部逻辑,否则所有嵌套结构都会被整体替换。
为什么 map-merge 合并后子键丢失
它只做一层合并,遇到同 key 的 map 就直接覆盖,不递归检查内部键。比如:
$colors: (primary: (#007bff, #0056b3));<br>$new: (primary: (#007bff, #0056b3, #004085));<br>$merged: map-merge($colors, $new); // 结果里 primary 就是那个三元 list,旧值彻底消失
- 这不是 bug,是设计行为
- 如果你期望保留原
primary里的 hover 色、disabled 色,再叠加新值,map-merge做不到 - 常见于想扩展
$theme-colors中某个主题色的 state(hover/active/disabled),结果把整个 color 值替换了
如何手写一个 map-deep-merge 函数
必须用递归逻辑判断每个 value 类型:是 map 就继续 merge,否则覆盖。Dart Sass 支持函数递归,但要注意避免无限循环(需加 base case):
@use "sass:map";<br><br>@function map-deep-merge($a, $b) {<br> $result: $a;<br> @each $key, $value in $b {<br> @if map-has-key($a, $key) and type-of(map-get($a, $key)) == "map" and type-of($value) == "map" {<br> $result: map-merge($result, ($key: map-deep-merge(map-get($a, $key), $value)));<br> } @else {<br> $result: map-merge($result, ($key: $value));<br> }<br> }<br> @return $result;<br>}
- 必须先
@use "sass:map"才能用map-has-key和map-get -
type-of()是关键,用来区分普通值和嵌套 map - 不要在 Bootstrap 主题定制里直接用这个函数改
$theme-colors——它仍需放在@import "bootstrap/scss/functions"之后、@import "bootstrap/scss/variables"之前,否则无效
更稳妥的替代方案:分层定义 + 显式注入
与其硬刚深度合并,不如把结构拆开,让每层职责清晰:
- 用独立变量存状态色:
$btn-primary-states: ("hover": #0056b3, "disabled": #6c757d); - 在
_buttons.scss或自定义 mixin 中手动读取:background-color: map-get($btn-primary-states, hover); - 主题色 map 保持扁平:
$theme-colors: (primary: #007bff, brand: #2563eb);,避免往里塞嵌套 - 如果真要多层主题(light/dark),用
@each $mode, $config in $themes分别生成规则,而不是依赖一次 merge 后全局生效
真正容易被忽略的是:深度合并不是“有了函数就万事大吉”,它放大了作用域和执行时机问题——哪怕函数写对了,只要调用位置错一拍,生成的 CSS 就还是旧的。调试时加一行 @debug $theme-colors; 看终端输出,比反复刷新页面更快。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











