为何用Nuxt transition实现的下拉列表在Firefox中比Chrome运行慢
问题根源
你当前使用setInterval每5ms逐帧修改元素height属性实现过渡的方案存在天生的性能缺陷:
- Chrome对高频JS驱动的DOM样式变更的优化优先级更高,Firefox对这类操作的调度策略不同,且默认会对小于10ms的
setInterval做延迟处理,你设置的5ms间隔实际在Firefox中执行频率更低,直接导致动画变慢。 - 每次修改
height都会触发页面重排,叠加高频执行的定时器,Firefox的渲染线程压力远大于Chrome,表现为动画卡顿、速度慢。
优化方案
方案1(最推荐):改用CSS transition实现过渡,彻底抛弃JS逐帧动画
CSS过渡由浏览器渲染线程直接处理,会优先调用GPU加速,性能远高于JS驱动的动画,两个浏览器的表现一致性也更高:
- 给下拉列表的ul元素添加样式:
.coins-dropdown { transition: height 0.3s ease; overflow: hidden; }
- 原有自定义过渡逻辑大幅简化,不需要逐帧修改高度:
beforeEnter(el) { el.style.height = 0; el.style.overflow = "hidden"; }, enterCoins(el, done) { let maxHeight; if (this.live_prices.length <= 3 && this.live_prices.length > 0) { maxHeight = this.live_prices.length * 56 } else { maxHeight = 224 } // 直接赋值最终高度,CSS自动完成过渡 el.style.height = `${maxHeight}px` // 过渡完成后调用done,也可以监听transitionend事件触发,更精准 setTimeout(done, 300) }, leave(el, done) { el.style.height = 0 setTimeout(done, 300) }
方案2(保留JS动画的选择):替换setInterval为requestAnimationFrame
如果你一定要用JS控制动画每帧的逻辑,把定时器替换为requestAnimationFrame,这个API会跟着浏览器的刷新帧率(一般60帧/秒,每帧间隔约16ms)执行,不存在定时器间隔不准的问题,Firefox和Chrome的调度逻辑一致:
把你现有的increaseHeight方法修改如下,leave逻辑同理修改即可:
increaseHeight(el, done, maxHeight) { cancelAnimationFrame(this.leaveAnimId); const increaseHeight = () => { let elHeight = parseInt(el.style.height) if (elHeight < maxHeight) { // 每帧加8px,300ms左右完成224px高度的动画,速度可以自己调整 const height = `${parseInt(el.style.height) + 8}px`; el.style.height = height; this.enterAnimId = requestAnimationFrame(increaseHeight) } else { cancelAnimationFrame(this.enterAnimId); done(); } }; this.enterAnimId = requestAnimationFrame(increaseHeight) }
额外优化建议
可以给下拉ul元素添加CSS属性will-change: height,通知浏览器提前对该元素的高度变化做优化,进一步降低重排带来的性能消耗。
另外注意你模板代码中的img标签上存在单独的v-else,没有对应的前置v-if条件,属于语法错误,建议检查修复。
内容的提问来源于stack exchange,提问作者Mehdi Rafiee
相关产品推荐
相关产品推荐

