[vue] 子组件动态更新,父组件向子组件Prop传递实时更新

时间:2025-04-23 22:41:16

今天工作遇到一个问题,vue中,父组件调用了子组件,初始化的时候可以正确显示。需要实时监控后台数据,向子组件传递,更新页面。

百度了一下,说使用watch可以监听。

试了一下,发现并不能改变。debug模式看了一下,数据已经接收到了,因为子组件还有孙子组件。这部分没有进行监听,修改了孙子组件,发现可以在页面上看到效果了。

这时候,日志中会报错

Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "startVal"

发现是因为子组件中如果直接更新操作prop值,有风险,可能会影响到父组件,所以在子组件中建变量来接收存储prop值的动态更新,不直接去修改prop值,问题解决

父组件

<style lang="less">
@import "./";
@import "../../styles/";
</style>

<template>
    <div>
        <div v-show="currNav=='xboot'">
            <h1>欢迎来到后台管理</h1>
        </div>
        <!-- <div v-show="currNav=='xboot-show'"><show/>
        </div> -->
        <div v-show="currNav=='onemap'">
             <Row :gutter="10">
                <Col :md="24">
                    <Row :gutter="5">
                        <Col :xs="24" :sm="12" :md="6" :style="{marginBottom: '10px'}">
                        <infor-card id-name="user_created_count" :end-val="" iconType="ios-hammer" color="#2d8cf0" intro-text="当前服务器总量"></infor-card>
                        </Col>
                        <Col :xs="24" :sm="12" :md="6" :style="{marginBottom: '10px'}">
                        <infor-card id-name="visit_count" :end-val="" iconType="ios-contact" color="#64d572" :iconSize="50" intro-text="数据库总量"></infor-card>
                        </Col>
                        <Col :xs="24" :sm="12" :md="6" :style="{marginBottom: '10px'}">
                        <infor-card id-name="collection_count" :end-val="" iconType="md-navigate" color="#ffd572" intro-text="导出操作执行中总量"></infor-card>
                        </Col>
                        <Col :xs="24" :sm="12" :md="6" :style="{marginBottom: '10px'}">
                        <infor-card id-name="transfer_count" :end-val="" iconType="md-podium" color="#f25e43" intro-text="导出操作成功总量"></infor-card>
                        </Col>
                    </Row>
                </Col>
            </Row>
            <Row :gutter="10">
               
                <Card>
                    <p slot="title" class="card-title">
                        <Icon type="md-map"></Icon>
                        执行状态
                    </p>
                    <div class="data-source-row">
                        <visite-volume></visite-volume>
                    </div>
                </Card>
            </Row>
        </div>
    </div>
</template>

<script>
import cityData from "./map-data/";
import homeMap from "./components/";
import visiteVolume from "./components/";
import userFlow from "./components/";
import countUp from "./components/";
import inforCard from "./components/";
import Cookies from "js-cookie";
import { getTotal } from "@/api/index";


export default {
  name: "home",
  components: {
    homeMap,
    visiteVolume,
    userFlow,
    countUp,
    inforCard
  },
  data() {
    return {
      showVideo: false,
      count: {
        createUser: 0,
        visit: 0,
        collection: 0,
        transfer: 0
      },
      cityData: cityData,
      newToDoItemValue: "",
      city: "",
      weather: "",
      username: ""
    };
  },
  computed: {
    currNav() {
      return this.$;
    },
    avatarPath() {
      return ;
    }
  },
  methods: {
      getTotal() {
          let that = this;
          getTotal("").then(res => {
              if ( == true) {
                   = ;
                   = ;
                   = ;
                   = ;
              }
          });
      },
      timer() {
          return setInterval(()=>{
              ();
          },5000)
      }
  },
  mounted() {
      ();
      ();
  },
  destroyed(){
        clearInterval();
    }
};
</script>

 

子组件

<style lang="less">
    @import './styles/';
</style>

<template>
    <Card :padding="0">
        <div class="infor-card-con">
            <Col class="infor-card-icon-con" :style="{backgroundColor: color, color: 'white'}" span="8">
                <Row class="height-100" type="flex" align="middle" justify="center">
                    <Icon :type="iconType" :size="iconSize"></Icon>
                </Row>
            </Col>
            <Col span="16" class="height-100">
                <Row type="flex" align="middle" justify="center" class="height-100">
                    <count-up 
                        class="infor-card-count user-created-count" 
                        :id-name="idName" 
                        :end-val="endValue2"
                        :start-val="startValue2"
                        :color="color"
                        :countSize="countSize"
                        :countWeight="countWeight"
                    >
                        <p class="infor-intro-text" slot="intro">{{ introText }}</p>
                    </count-up>
                </Row>
            </Col>
        </div>
    </Card>
</template>

<script>
import countUp from './';

export default {
    name: 'inforCard',
    components: {
        countUp
    },
    watch: {
        endVal(newValue, oldValue) {
            //注意这里接收,不能直接更新prop值,需要定义变量,否则会提示有风险更新父组件
            this.endValue2 = newValue
            this.startValue2 = oldValue
            //('子组件inforcard接收到new:'+newValue+"  old:"+oldValue);
        },
    },
    data() {
        return {
            endValue2:0,
            startValue2:0
        };
    },
    props: {
        idName: String,
        endVal: Number,
        startVal:{
            type: Number,
            default: 0
        },
        color: String,
        iconType: String,
        introText: String,
        countSize: {
            type: String,
            default: '30px'
        },
        countWeight: {
            type: Number,
            default: 700
        },
        iconSize: {
            type: Number,
            default: 40
        }
    }
};
</script>

子组件的子组件

<template>
    <div>
        <p :class="className" :style="{textAlign: 'center', color: color, fontSize: countSize, fontWeight: countWeight}"><span v-cloak :>{{ startVal }}</span><span>{{ unit }}</span></p>
        <slot name="intro"></slot>
    </div>
</template>

<script>
import CountUp from 'countup';

function transformValue (val) {
    let endVal = val;
    let unit = '';
    return {
        val: endVal,
        unit: unit
    };
}

export default {
    data () {
        return {
            unit: '',
            demo: {}
        };
    },
    name: 'countUp',
    props: {
        idName: String,
        className: String,
        startVal: {
            type: Number,
            default: 0
        },
        endVal: {
            type: Number,
            required: true
        },
        decimals: {
            type: Number,
            default: 0
        },
        duration: {
            type: Number,
            default: 2
        },
        delay: {
            type: Number,
            default: 500
        },
        options: {
            type: Object,
            default: () => {
                return {
                    useEasing: true,
                    useGrouping: true,
                    separator: ',',
                    decimal: '.'
                };
            }
        },
        color: String,
        countSize: {
            type: String,
            default: '30px'
        },
        countWeight: {
            type: Number,
            default: 700
        },
        introText: [String, Number]
    },
    mounted () {
        this.$nextTick(() => {
            setTimeout(() => {
                let res = transformValue();
                let endVal = ;
                 = ;
                let demo = {};
                 = demo = new CountUp(, , endVal, , , );
                if (!) {
                    ();
                }
            }, );
        });
    },
    watch: {
        endVal (val) {
            let res = transformValue(val);
            let endVal = ;
             = ;
            let demo = {};
             = demo = new CountUp(, , endVal, , , );
            if (!) {
                ();
            }
            // (endVal);
        }
    }
};
</script>