React-Redux三元运算符切换内容时IE动画残留问题求助
React三元运算符切换加载状态时IE浏览器动画不停止的问题解决与根源分析
最近在React项目里遇到一个奇怪的兼容性问题:我用三元运算符控制加载状态,加载中显示loader动画,加载完成后替换成实际内容。这段逻辑在Chrome和Firefox里都正常工作,但在IE浏览器里,就算加载完成、内容已经显示出来了,原来的loader动画居然还在一直转!
问题复现代码
<div className="wrapper"> {isFetching ? <div className="loader large" /> : <div>{children}</div>} </div>
已经验证可行的两个修复方案
方案一:拆分独立的条件渲染
把三元运算符拆成两个&&条件,分别控制loader和内容的显示:
<div className="wrapper"> {isFetching && <div className="loader large" />} {!isFetching && <div>{children}</div>} </div>
方案二:避免使用自闭合div
给loader的div加个无关的内容(比如一个字符),让三元两边的div结构保持一致:
<div className="wrapper"> {isFetching ? <div className="loader large">a</div> : <div>{children}</div>} </div>
对应的Loader SASS样式
.loader { animation: spin 1s linear infinite; border: .5rem solid $grey; border-radius: 50%; border-top: .5rem solid $loader-border-color; height: 2rem; position: absolute; right: 2rem; top: 7.2rem; width: 2rem; z-index: 10; &.large { height: 7rem; margin: auto; position: relative; right: 0; top: 0; width: 7rem; } } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
深挖问题根源
折腾了一阵后,我搞清楚了问题出在IE下React虚拟DOM的diff算法对自闭合元素的处理差异:
当用三元运算符切换「自闭合无内容的div」和「有内容的div」时,React在IE里的diff过程会把这两个div判定为可以复用的同一个DOM节点,而不是直接卸载旧节点、挂载新节点。
具体来说:
- React的diff算法首先对比节点类型,发现都是div标签,又没有设置key,就会尝试复用现有节点,只更新它的className和内容。
- 但loader的CSS动画是绑定在这个DOM节点上的,IE对CSS动画的终止逻辑和现代浏览器不一样——就算移除了
loader类,已经启动的动画还是会继续跑下去,不会自动停止。
而两个修复方案都是绕开了这种“节点复用”:
- 方案一用两个独立的条件渲染,让React在状态切换时彻底卸载loader节点,同时挂载内容节点,自然就把带动画的元素彻底移除了。
- 方案二让两边的div都有内容,这样React的diff算法会更彻底地更新节点,包括清除之前的动画状态,不会再复用带动画的旧节点。
如果项目里已经大量用了三元运算符,不想大规模改代码,还有个更简单的办法:给两个分支的节点加不同的key,强制React卸载旧节点、挂载新节点,比如:
<div className="wrapper"> {isFetching ? <div key="loading-loader" className="loader large" /> : <div key="loading-content">{children}</div>} </div>
只需要加个唯一的key,不用改结构,就能解决IE里动画停不下来的问题。
内容的提问来源于stack exchange,提问作者Diazole
相关产品推荐
相关产品推荐

