开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第3天,点击查看活动详情
为什么需要管理z-index
多人开发的复杂项目中,对于定位元素(如弹窗、下拉或者固定的头部),我们很难知道别人写了多高的z-index,有时候想弹窗一个弹窗,却发现别人写了 z-index: 9999999999,于是我们想要覆盖它,只能在后面再加一个9,长此以往,层级将会变得凌乱难以维护。
要解决的问题
- 统一一个地方管理z-index
- 校验用法是否正确
方案一:scss的list取下标
定义一个scss数组,数组的值是对应层级的命名:
$elements: header, footer, dialog;
设置z-index时,我们通过scss函数 index 获取数组下标。
.dialog {
z-index: index($elements, dialog); // 3
}
如此,我们只需要规定书写样式时,统一用 elements 即可,很容易看出,z-inde越大需要放到约后面,当有其他z-index需要插入到前面时,编译时会将所有引用 $elements 的index都更新到。
错误警告:当引用了一个不存在的z-index命名时,我们希望输出警告给开发者
@function z($list, $element) {
$z-index: index($list, $element);
@if $z-index {
@return $z-index;
}
@warn '#{$list} 中存在#{$element}层级';
@return null;
}
缺陷: 层级定义不灵活,根据数组的index来确定z-index,当你想定义比较大的层级时(如99),几乎是不可能的了,所以这个方案几乎不能派上实际用途,一般ui组件库的z-index都难以覆盖。
方案二:scss的map取键值
方法二可以视为方法一的升级,相当于用 Object 的键值对代替了数组的下标和值。
先定义一个scss的map变量:
$z-layer: (
"header": 9,
"footer": 9,
"dialog": 99,
);
使用z-index时,使用scss中的map-get方法来获取值:
.diglog {
z-index: map.get($z-layer, "dialog"); // 99
}
封装成函数
$z-layer: (
"header": 19,
"dialog": 99
);
@function z($key) {
@if not map-has-key($z-layer, $key) {
@warn "No layer found for `#{$key}` in $z-layer map. Property omitted.";
@return null;
}
@return map-get($z-layer, $key);
}
如此一来,我们可以更自由的定义z-index,随时结合ui组件库的z-index做出调整。
定义通用z-index
如果仅仅根据业务来定义z-index,很容易出现z-index越写越大的情况,所以需要先预设一套z-index规则,限制某类元素最大z-index。
同时,一般项目都会使用组件库,所以这个规则需要配合组件库的z-index来制定,否则会出现组件无法被遮挡的尴尬窘境。
如ant-design-vue,组件层级为:
伪类元素:1
affix组件(吸顶):10
drawer、modal: 1000
message、notification: 1010
popover:1030
dropdown 、cascader、autoComplete、DatePicker、select、TimePicker、treeSelect: 1050
Popconfirm:1060
tooltip: 1070
其他: < 5
结合ant-design-vue层级规范,我们业务中的组件层级可以定义为:==标准值[-区间值]==
伪类:1[-4]
页面绝对定位元素 5[-9]
页面吸顶/固定定位元素:10[-20]
drawer/modal: 1000[-1009]
message/notification: 1010[-1029]
popover:1030[-1039]
dropContent: 1050[-1059]
popconfirm: 1060[-1069]
tooltip: 1070[-1079]
activityDialog: 1080[-1099] 活动弹窗,同一时间应该保持只存在一个活动弹窗,
toast:2080[-2099] 反馈提示,应该高于所有
$z-layer: (
"before": 1,
"after": 1,
"absolute": 5,
"fixed": 10,
"modal": 1000,
"message": 1010,
"popover": 1060,
"drop": 1050,
"confirm": 1060,
"tooltip": 1070,
"activity": 1080,
"toast": 2080
);
@function z($key) {
@if not map-has-key($z-layer, $key) {
@warn "No layer found for `#{$key}` in $z-layer map. Property omitted.";
@return null;
}
@return map-get($z-layer, $key);
}
添加scss函数到全局
vite通过 css-preprocessoroptions 配置实现全局scss文件配置
vue-cli通过 css.loaderOptions 配饰实现全局scss文件配置
解决stylelint报错
如果安装了stylelint,很可能会出现这样的提示:
Unexpected unknown function "z" (funciton-no-unknown)
我们可以通过 function-no-unknown配置解决:
找到stylelint配置,ignoreFunctions为忽略这个函数的报错,相当于定义了全局函数:
{
"stylelint": {
// ...
"rules": {
// ...
"function-no-unknown": [true, {
"ignoreFunctions": ["z"]
}]
}
}
}
配合stylelint检查
既然制定了z-index规范,当然是希望在书写的时候都通过函数的方法去定义z-index,但是项目成员多的时候,难免会出现直接写 z-index: 9999999;的情况,为此,如果可以通过stylelint去限制直接写数字就最好不过了
通过stylelint的插件 stylelint-declaration-strict-value 可以实现我们的功能:
安装
pnpm add stylelint-declaration-strict-value -D
配置
增加 plugins 和 rules,其中rules 只允许z-index设置为z函数,不能使用变量("ignoreVariables": false,),不能直接设置数值(默认值"ignoreValues": null)
{
"stylelint": {
// ...
"plugins": ["stylelint-declaration-strict-value"],
"rules": {
// ...
"function-no-unknown": [true, {
"ignoreFunctions": ["z"]
}],
"scale-unlimited/declaration-strict-value": [
"z-index",
{
"ignoreVariables": false,
"ignoreFunctions": {
"z": true
}
}
]
}
}
}
语法提示
如果在编辑代码的时候,提示 z函数,并且把 $z-layer 值作为悬浮选项值,那真的太方便了,可惜没找到这样的vscode插件,也许需要自己去编写一个插件可以实现。
唯一的语法提示来自:SCSS IntelliSense 插件,在输入z()可以提示入参名字。