问题描述
最近,测试给提了一个bug,在表单组件的某个子项前出现了一个冒号:
问题分析
作为一个刚进公司的菜鸟,第一反应可能是公司根据Element Plus二次封装的组件库出问题了,直接打开组件库一头扎进表单组件。好家伙,近两千行的.vue
文件差点给我干蒙了。
好一阵寻找才发现,表单组件的布局是编写在另一个formLayout.js
文件里,终于在该文件中找到了表单组件的渲染逻辑(为使代码简洁删除了部分属性)。
js
代码解读
复制代码
... <el-form-item label-width={labelWidth} ref={itemRef} // label={showLabel ? config.label : ''} style={itemStyle} v-slots={{ label: () => { return <span> <el-popover placement="top" width="200" trigger="hover" v-slots={{ reference: () => { return config.tips?<el-icon class='tip-style'><Warning /></el-icon>:null } }} > <span class='tip-style-span'>{config.tips}</span> </el-popover> {showLabel ? config.label : ''} </span> } }} > ...
其中这个label
属性引起了我的注意,原来是某个同事根据需求将原来的label
属性用插槽重写,在label
前增加了一个气泡弹框。但是在插槽中根据showLabel
的值来决定是否渲染label
的逻辑并没有改变,那为啥会多一个冒号呢?
这个bug看起来就像隐藏了该表单子项的label
文本但没隐藏冒号,毕竟原来使用label
属性时没出现问题啊?怀着试一试的心情,我把插槽注释掉,仍采用原来的逻辑使用label
属性来渲染,这时奇迹出现了:
冒号居然消失了,看来是使用插槽带来的问题,但为什么会这样呢?(暂且埋个伏笔~~~)
问题解决
虽然问题出在插槽上,可也不能不顾需求不使用插槽来渲染气泡框啊,这要咋办呢?转念一想既然问题的根源出在冒号上,那就去找找这个冒号是在哪里定义的。通过定位页面上的冒号,找到了冒号定义的逻辑:
原来是通过一个伪元素::after
来增加的,在代码中全局搜索content: ':'
(ps:全局搜索yyds!!!),终于找到了冒号产生的根源:
css
代码解读
复制代码
... .el-form-item__label { &::after { content: ':'; } } ...
既然找到了冒号产生的原因,那目前的一个思路就是找到label
这个节点或组件,并根据showLabel
的值来判断是否动态渲染这个CSS
属性就行了。说干就干,又准备在全局搜索el-form-item__label
这个类名,看看是在哪个DOM
节点或组件定义的。结果尴尬了,根本就找不到该类名定义的地方,原来这是Element Plus
内部定义的类名。
作为一个菜鸟这时我是没辙了,毕竟总不能找到依赖文件中去给这个组件加判断吧?还好在请教公司大佬后得到回复:“可以把伪元素定义的冒号放在label
插槽里的span
标签上。”一语惊醒梦中人,找不到label
节点我还不能换个能找到的吗?修改后的代码如下:
js
代码解读
复制代码
... <el-form-item label-width={labelWidth} ref={itemRef} // label={showLabel ? config.label : ''} style={itemStyle} v-slots={{ label: () => { // 根据showLabel判断是否显示伪元素冒号: return <span class={showLabel ? 'el-form-item__label__span' : ''}> <el-popover placement="top" width="200" trigger="hover" v-slots={{ reference: () => { return config.tips?<el-icon class='tip-style'><Warning /></el-icon>:null } }} > <span class='tip-style-span'>{config.tips}</span> </el-popover> {showLabel ? config.label : ''} </span> } }} > ...
CSS
中注释掉原类名,在span
标签的类名上添加伪元素::after
,代码如下:
css
代码解读
复制代码
... /*.el-form-item__label { &::after { content: ':'; } }*/ .el-form-item__label__span { &::after { content: ':'; } } ...
这下可解决问题了,大功告成!
伏笔回收
虽然解决了冒号的bug,可开头的问题还是使我困惑,为啥用插槽替换属性时就会出现冒号?带着这个疑问,我反复对比了label
使用属性和插槽渲染时页面的DOM
节点,终于让我发现了端倪:
使用label
属性渲染:
使用label
插槽渲染:
当渲染条件showLabel === false
时,使用label
属性会使label
的DOM
节点不会被渲染在DOM
树上(v-if也给了提示);而使用label
插槽会使label
的DOM
节点仍被渲染在DOM
树上。 这也就间接导致了虽然label
的值为空不会被渲染,但定义在label
节点上的::after
伪元素冒号仍然会被渲染。
到这里我恍然大悟,原来一切都是因为label
值为空仍被渲染导致的,所以我又想到label
值为空时能不能直接把label
节点干掉,这样既能和原来的逻辑保持一致,又能减少DOM
节点的渲染提高性能(有一丢丢丢提升吗?)。
想到这实现也就不难了:保持原有的CSS
渲染逻辑,根据showLabel
的值在插槽中决定要不要渲染label
节点(考虑可读性可以将label
的渲染结构抽出去)。代码如下:
js
代码解读
复制代码
... <el-form-item label-width={labelWidth} ref={itemRef} // label={showLabel ? config.label : ''} style={itemStyle} v-slots={{ // 根据showLabel判断是否渲染label标签 label: showLabel ? () => { return <span> <el-popover placement="top" width="200" trigger="hover" v-slots={{ reference: () => { return config.tips?<el-icon class='tip-style'><Warning /></el-icon>:null } }} > <span class='tip-style-span'>{config.tips}</span> </el-popover> {showLabel ? config.label : ''} </span> } : null // 这里值为''空字符串时DOM仍会渲染label节点? }} > ...
查看页面结构,成功消灭label
节点:
总结
Element Plus
中表单子项el-form-item
的label
属性的值为空时不会被渲染在DOM
树上,而在v-slots
插槽中label
的值为空时仍会被渲染在DOM
树上。- 在插槽中
label
属性的值要赋为null
才不会被渲染在DOM
树上,属性中传入空字符串''
就可以。
原文链接:https://juejin.cn/post/7419532382803410994