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

Vue3初始渲染后prop值变化时组件重新渲染的问题

问题根源

你的思路方向是对的,但遗漏了watch默认不会触发初始值这个点——mounted里注册的watch,只会在endvalue发生变化时执行回调,组件第一次挂载时的初始prop值不会触发它,所以初始渲染没效果;去掉watch直接在mounted里处理,又没监听prop变化,自然无法响应更新。

修复方案

把进度更新的逻辑抽成独立方法,既在mounted里调用处理初始值,又通过watch监听prop变化时调用,同时要注意清除旧定时器,避免多个定时器冲突:

步骤1:提取复用方法

在methods里新增updateProgress方法,把原来watch里的逻辑移进去,同时修复原代码里的错误(比如误用this.newEndValue,应该用传入的目标值):

methods: {
  updateProgress(targetValue) {
    const progressBar = this.$refs.progressBar;
    const valueContainer = this.$refs.valueContainer;
    
    // 先清除之前的定时器,避免叠加
    if (this.progressTimer) {
      clearInterval(this.progressTimer);
    }

    let progressValue = 0;
    const speed = 10;

    this.progressTimer = setInterval(() => {
      if (targetValue < 0) {
        valueContainer.innerText = '---';
        clearInterval(this.progressTimer);
        return;
      }

      if (targetValue !== 0) {
        progressValue++;
      }
      
      valueContainer.innerText = `${progressValue}%`;

      if (progressValue >= 100) {
        valueContainer.style.color = 'green';
        progressBar.style.background = 'green';
      } else {
        valueContainer.style.color = 'black';
        progressBar.style.background = `conic-gradient( 
          #192A44 ${progressValue * 3.6}deg,
          #EEEEEE ${progressValue * 3.6}deg
        )`;
      }

      // 到达目标值或超过100时停止
      if (progressValue >= targetValue || progressValue >= 100) {
        clearInterval(this.progressTimer);
      }
    }, speed);
  }
}

步骤2:初始化与监听

在data里添加定时器变量,然后在mounted里调用初始值,同时注册watch:

data() {
  return {
    progressTimer: null // 存储定时器ID,用于清除
  }
},
mounted() {
  // 初始渲染时调用,传入初始prop值
  this.updateProgress(this.endvalue);
  
  // 监听prop变化
  this.$watch('endvalue', (newEndValue) => {
    this.updateProgress(newEndValue);
  });
}

额外优化:组件销毁时清除定时器

添加beforeUnmount钩子,避免组件销毁后定时器还在运行:

beforeUnmount() {
  if (this.progressTimer) {
    clearInterval(this.progressTimer);
  }
}
修改后的完整代码
<template>
    <div class="progressDisplay">
        <div class="progressBar" ref="progressBar">
            <div class="coverCircle">
                <div class="valueContainer" ref="valueContainer"></div>
            </div>
        </div>
    </div>
</template>
<script>
export default {
    name: 'ProgressCircle',
    props: {
        endvalue: {
            type: Number,
            required: true
        }
    },
    data() {
        return {
            progressTimer: null
        }
    },
    methods: {
        updateProgress(targetValue) {
            const progressBar = this.$refs.progressBar;
            const valueContainer = this.$refs.valueContainer;
            
            if (this.progressTimer) {
                clearInterval(this.progressTimer);
            }

            let progressValue = 0;
            const speed = 10;

            this.progressTimer = setInterval(() => {
                if (targetValue < 0) {
                    valueContainer.innerText = '---';
                    clearInterval(this.progressTimer);
                    return;
                }

                if (targetValue !== 0) {
                    progressValue++;
                }
                
                valueContainer.innerText = `${progressValue}%`;

                if (progressValue >= 100) {
                    valueContainer.style.color = 'green';
                    progressBar.style.background = 'green';
                } else {
                    valueContainer.style.color = 'black';
                    progressBar.style.background = `conic-gradient( 
                        #192A44 ${progressValue * 3.6}deg,
                        #EEEEEE ${progressValue * 3.6}deg
                    )`;
                }

                if (progressValue >= targetValue || progressValue >= 100) {
                    clearInterval(this.progressTimer);
                }
            }, speed);
        }
    },
    mounted() {
        this.updateProgress(this.endvalue);
        this.$watch('endvalue', (newEndValue) => {
            this.updateProgress(newEndValue);
        });
    },
    beforeUnmount() {
        if (this.progressTimer) {
            clearInterval(this.progressTimer);
        }
    }
}
</script>
<style scoped>
.progressDisplay {
    display: flex;
    justify-content: center;
    align-self: center;
    height: 300px;
    width: 300px;
}

.progressBar {
    height: 250px;
    width: 250px;
    border-radius: 50%;
    background-color: #EEEEEE;
    display: grid;
    place-items: center;
    -webkit-box-shadow: inset -8px -2px 3px -1px rgba(0, 0, 0, 0.02);
    -moz-box-shadow: inset -8px -2px 3px -1px rgba(0,0,0,0.02);
    box-shadow: inset -8px -2px 3px -1px rgba(0, 0, 0, 0.068);
}

.coverCircle {
    height: 200px;
    width: 200px;
    display: grid;
    place-items: center;
    background-color: white;
    border-radius: 50%;
    -webkit-box-shadow: -8px -2px 43px -1px rgba(0,0,0,0.31);
    -moz-box-shadow: -8px -2px 43px -1px rgba(0,0,0,0.31);
    box-shadow: -8px -2px 2px rgba(0, 0, 0, 0.077);
}

.valueContainer {
    position: relative;
    font-size: 34px;
    font-weight: 500;
}
</style>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 14:23:11