You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue加载提示需1ms延迟才显示的原因及最佳实践咨询

为什么Vue加载提示需要1ms延迟才显示?

问题背景

我实现了一个简单的Vue加载提示:

<div v-show="isLoading" class="preloader">Loading...</div>

data() {
  return {
    isLoading: false
  }
},

在调用Google Maps API添加地图标记的子组件方法中,通过$emit控制该状态:

methods: {
  async initMarkers() {
    this.$emit('loading', true)
    const map = this.map
    const { AdvancedMarkerElement } = await google.maps.importLibrary('marker')

    try {
      this.members.map((member) => {
        const marker = new AdvancedMarkerElement({
          map,
          title: member.ttl,
          position: {
            lat: parseFloat(member.lat),
            lng: parseFloat(member.lng)
          }
        })

        this.markers.push(marker)
      })
    } catch (error) {
      console.error('Error loading marker library', error)
    } finally {
      this.$emit('loading', false)
    }
  },
}

该过程耗时约2秒,但加载提示始终不显示,仅当添加至少1ms的延迟后才会出现:

this.$emit('loading', true)
await new Promise((resolve) => setTimeout(resolve, 1))
try{

想了解为何需要这个延迟,以及该做法是否属于不良实践。


原因分析

核心问题在于Vue的响应式DOM更新机制和JavaScript事件循环的执行顺序:

  • 当你调用this.$emit('loading', true)时,父组件的isLoading状态会被更新,但Vue并不会立刻触发DOM更新——它会把所有状态变更收集起来,等到当前同步代码执行完毕后,才会在同一个事件循环的"更新阶段"批量处理DOM渲染。
  • 紧接着的await google.maps.importLibrary('marker')虽然是异步操作,但它的等待是在当前同步代码执行完后立刻进入的。这就导致Vue还没来得及完成isLoading对应的DOM更新(显示加载提示),就进入了API加载的等待,而API加载完成后又立刻把isLoading设为false,所以加载提示根本没机会渲染出来。
  • 添加await setTimeout(resolve, 1)相当于把后续的API调用代码推到下一个事件循环tick中执行。这时候当前tick的同步代码已经执行完,Vue的DOM更新流程已经完成,加载提示成功渲染,之后才开始执行API加载逻辑。

这种做法是否是不良实践?

不算绝对的不良,但属于临时 workaround,有更优雅的替代方案:

  1. 使用Vue的nextTick
    利用Vue提供的nextTick方法,它会在DOM更新完成后执行回调,比setTimeout更贴合Vue的生命周期:
    async initMarkers() {
      this.$emit('loading', true)
      // 等待DOM更新完成后再执行后续逻辑
      await this.$nextTick()
      const map = this.map
      const { AdvancedMarkerElement } = await google.maps.importLibrary('marker')
      // ... 后续代码不变
    }
    
  2. 拆分异步逻辑
    把状态更新和异步API调用明确分开,确保状态更新的DOM渲染有足够时间完成:
    async initMarkers() {
      this.$emit('loading', true)
      // 先触发状态更新,再单独处理API逻辑
      await this.loadMarkers()
    },
    async loadMarkers() {
      const map = this.map
      const { AdvancedMarkerElement } = await google.maps.importLibrary('marker')
      try {
        // ... 标记创建逻辑
      } catch (error) {
        console.error('Error loading marker library', error)
      } finally {
        this.$emit('loading', false)
      }
    }
    

总结

1ms延迟的本质是利用事件循环的特性,给Vue的DOM更新留出时间,但用nextTick更符合Vue的设计理念,也更可靠。如果只是临时解决问题,setTimeout的方式也能工作,但长期来看建议换成更优雅的方案。

内容的提问来源于stack exchange,提问作者marcelo2605

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 14:37:12