Stencil组件条件渲染失效及图标出现时元素抖动问题求助
问题描述
我最初用这段代码实现复选框的勾选图标:
{this.checked && <ifx-icon icon="check-12"></ifx-icon>}
代码能正常运行,但每次checked变为true时,ifx-icon出现会导致整个元素上下小幅抖动。我试过把父元素设为inline-flex、用vertical-align、调整line-height等方法,都没解决。
之后我尝试让ifx-icon始终存在,只动态切换icon属性:
{<ifx-icon icon={this.checked ? "check-12" : ""}></ifx-icon>}
但此时哪怕checked为true,图标也完全不渲染,还会弹出我自定义的“icon not found”错误。
接着我试了条件渲染两个ifx-icon组件:
{this.checked ? <ifx-icon icon="check-12"></ifx-icon> : <ifx-icon icon="asd"></ifx-icon>}
同样不行,checked为true时还是会出现“icon not found”错误。但如果把else分支换成纯文本:
{this.checked ? <ifx-icon icon="check-12"></ifx-icon> : 'asd'}
代码能运行,但抖动问题依然存在。
请问:
- 怎么消除元素抖动?
- 另外两种条件渲染方式失效的原因是什么?
相关代码
CSS样式
ifx-icon:empty { display: none; } :host { border: 1px solid; display: inline-flex; } .checkbox__container { display: inline-flex; flex-direction: row; align-items: center; padding: 0px; gap: 8px; margin: 0; & .checkbox__wrapper { display: flex; justify-content: center; align-items: center; width: 20px; height: 20px; background-color: tokens.$color-bg-white; border: 1px solid #575352; border-radius: 1px; flex: none; order: 0; flex-grow: 0; & .icon__wrapper { display: flex; justify-content: center; align-items: center; height: 100%; } &:hover { background-color: #EEEDED; border: 1px solid #575352; border-radius: 1px; flex: none; order: 0; flex-grow: 0; } &.disabled { background-color: #BFBBBB; border-radius: 1px; flex: none; order: 0; flex-grow: 0; } &.checked { background-color: #0A8276; border-radius: 1px; flex: none; order: 0; flex-grow: 0; color: tokens.$color-white; &:hover { background-color: #08665C; border-radius: 1px; flex: none; order: 0; flex-grow: 0; } } } }
组件render代码
render() { return ( <div class="checkbox__container" onClick={this.handleCheckbox.bind(this)}> <div class={`checkbox__wrapper ${this.checked ? 'checked' : ""} ${this.disabled ? 'disabled' : ""}`}> <div class="icon__wrapper"> {<ifx-icon icon={this.checked ? "check-12" : ""}></ifx-icon>} </div> </div> <div class="label"> <slot /> </div> </div> ); }
消除元素抖动的方案
抖动的核心原因是元素渲染/移除时改变了容器的布局流,或者图标本身带默认外边距、行高导致布局偏移。以下是几种可行的解决方法:
1. 固定占位,用样式控制图标显隐
保持ifx-icon始终存在,通过CSS的visibility或opacity控制显示,而非移除元素——这样容器尺寸不会变化,彻底避免抖动:
<ifx-icon icon="check-12" class={this.checked ? "" : "icon-hidden"} ></ifx-icon>
添加对应的CSS:
.icon-hidden { visibility: hidden; /* 隐藏但保留占位空间 */ /* 也可以用 opacity: 0; 视觉隐藏但占位 */ }
2. 给图标容器锁死尺寸
你的checkbox__wrapper已经是20x20固定尺寸,但icon__wrapper只设了height:100%,补充宽度并限制图标尺寸:
.icon__wrapper { display: flex; justify-content: center; align-items: center; width: 100%; /* 和父容器宽度一致 */ height: 100%; } ifx-icon { display: block; width: 12px; /* 和check-12图标尺寸匹配 */ height: 12px; flex-shrink: 0; /* 防止图标被压缩变形 */ }
这样不管图标是否存在,容器尺寸都固定,不会引发布局偏移。
3. 绝对定位脱离文档流
把图标从布局流中剥离,它的显隐不会影响其他元素:
.checkbox__wrapper { position: relative; /* 父容器设为相对定位 */ /* 原有样式保留 */ } .icon__wrapper { position: absolute; top: 0; left: 0; right: 0; bottom: 0; display: flex; justify-content: center; align-items: center; }
图标会始终在容器内居中,显示/隐藏不会改变父容器的布局结构。
两种条件渲染失效的原因
1. 动态切换icon为空字符串时失效
你的CSS里有ifx-icon:empty { display: none; },当icon设为空时,ifx-icon组件因为找不到对应图标,内部不会渲染任何内容,触发:empty伪类被隐藏。同时组件自身对空icon属性做了错误处理,弹出“icon not found”提示。
2. 条件渲染两个ifx-icon时失效
这是组件实例复用导致的:框架会认为三元表达式里的两个ifx-icon是同一个组件,只会更新icon属性,不会销毁旧实例重新创建。当checked从false切到true时,组件内部可能还缓存着之前“icon not found”的状态,没有正确响应icon属性的更新。
如果要让这种方式生效,可以给两个图标加不同的key,强制框架销毁旧组件:
{this.checked ? <ifx-icon key="checked-icon" icon="check-12"></ifx-icon> : <ifx-icon key="unchecked-icon" icon="asd"></ifx-icon>}
不过这种方式依然可能存在抖动问题,不如固定占位的方案优雅。
内容的提问来源于stack exchange,提问作者happy_story

