在处理复杂的 Sass 架构时,使用 Sass 地图来维护配置和选项并不少见。有时你会看到地图嵌套在其他地图中(可能在多个层级上),就像 o-grid 中的这个例子
$o-grid-default-config: (
columns: 12,
gutter: 10px,
min-width: 240px,
max-width: 1330px,
layouts: (
S: 370px, // ≥20px columns
M: 610px, // ≥40px columns
L: 850px, // ≥60px columns
XL: 1090px // ≥80px columns
),
fluid: true,
debug: false,
fixed-layout: M,
enhanced-experience: true
);
这类地图的问题在于,从嵌套的树中获取和设置值并不容易。这绝对是你想隐藏在函数中的东西,以避免每次都手动进行。
深度获取
实际上,构建一个函数来从地图中获取深度嵌套的值非常容易。
/// Map deep get
/// @author Kitty Giraudel
/// @access public
/// @param {Map} $map - Map
/// @param {Arglist} $keys - Key chain
/// @return {*} - Desired value
@function map-deep-get($map, $keys...) {
@each $key in $keys {
$map: map-get($map, $key);
}
@return $map;
}
例如,如果我们想获取与我们配置地图中的 M
布局关联的值,只需像这样操作:
$m-breakpoint: map-deep-get($o-grid-default-config, "layouts", "M");
// 610px
注意,字符串周围的引号是可选的。我们只是为了可读性而添加它们。
深度设置
另一方面,构建一个函数来设置深度嵌套的键可能会非常繁琐。
/// Deep set function to set a value in nested maps
/// @author Kitty Giraudel
/// @access public
/// @param {Map} $map - Map
/// @param {List} $keys - Key chaine
/// @param {*} $value - Value to assign
/// @return {Map}
@function map-deep-set($map, $keys, $value) {
$maps: ($map,);
$result: null;
// If the last key is a map already
// Warn the user we will be overriding it with $value
@if type-of(nth($keys, -1)) == "map" {
@warn "The last key you specified is a map; it will be overrided with `#{$value}`.";
}
// If $keys is a single key
// Just merge and return
@if length($keys) == 1 {
@return map-merge($map, ($keys: $value));
}
// Loop from the first to the second to last key from $keys
// Store the associated map to this key in the $maps list
// If the key doesn't exist, throw an error
@for $i from 1 through length($keys) - 1 {
$current-key: nth($keys, $i);
$current-map: nth($maps, -1);
$current-get: map-get($current-map, $current-key);
@if $current-get == null {
@error "Key `#{$key}` doesn't exist at current level in map.";
}
$maps: append($maps, $current-get);
}
// Loop from the last map to the first one
// Merge it with the previous one
@for $i from length($maps) through 1 {
$current-map: nth($maps, $i);
$current-key: nth($keys, $i);
$current-val: if($i == length($maps), $value, $result);
$result: map-merge($current-map, ($current-key: $current-val));
}
// Return result
@return $result;
}
现在,如果我们想更新与我们配置地图中的 M
布局关联的值,我们可以执行以下操作:
$o-grid-default-config: map-deep-set($o-grid-default-config, "layouts" "M", 650px);
额外资源
上面的函数并不是解决这个问题的唯一方案。
Sassy-Maps 库还提供了 map-deep-set
和 map-deep-get
函数。同样,Kitty Giraudel 也编写了一个 jQuery 风格的 extend
函数 来使内置的 map-merge
递归,并且能够一次合并多个地图。
错别字
应该改为
不过代码片段很酷!
-From times to times
实际上应该改为
From time to time….
节省了大量时间,感谢 Hugo!
一如既往地有用!谢谢!
甚至还有 map-merge-deep() … 查看 Sassy-Maps 项目中的 PR。在我的项目中效果很好。 https://github.com/at-import/Sassy-Maps/issues/9
这是一个不错的开始!我使用了获取器并创建了一个类似命名空间的版本。
首先,我创建了一个函数,该函数将字符串按句点分割成键,然后从提供的地图中获取值。
然后,你可以创建辅助方法来访问各种配置地图,以抽象底层地图,例如以下方法:
@Travis 这真的很有帮助,谢谢!
抱歉样式问题(我不知道如何使其更美观)。
因此,我的“获取”函数变体可在任何情况下使用。
@function get($map, $paths...){
$allParts: ();
@each $path in $paths {
$pathParts: str-split($path, '.');
@each $pathPart in $pathParts {
$allParts: append($allParts, $pathPart);
}
}
@each $key in $allParts {
$map: map-get($map, $key);
}
@return $map;
}
使用
get($buttons, 'foo.bar.baz');
get($buttons, 'foo', 'bar', 'baz');
get($buttons, foo, bar, baz);
get($buttons, 'foo.bar', 'baz');
get($buttons, 'foo.bar', baz);
get($buttons, 'foo', 'bar.baz');
我对 map-deep-get 有点问题。在你的例子中,该函数获取单个特定值。但是,当我在 codepen 中尝试时,我遇到了错误。它似乎获取了所有键和值。 https://codepen.io/aalokt89/pen/NLzvpL
不错的函数,但它假定所有键都存在,而实际上可能并非如此。我稍微调整了一下,以防止这种情况,如果链中任何键未找到,则返回“null”。
@marc 很好!谢谢!我需要将其用作条件来检查键是否存在。