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

今天工作遇到一个问题,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 "./home.less";
@import "../../styles/common.less";
</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="count.createUser" 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="count.visit" 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="count.collection" 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="count.transfer" 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/get-city-value.js";
import homeMap from "./components/map.vue";
import visiteVolume from "./components/near-hour.vue";
import userFlow from "./components/userFlow.vue";
import countUp from "./components/countUp.vue";
import inforCard from "./components/inforCard.vue";
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.$store.state.app.currNav;
    },
    avatarPath() {
      return localStorage.avatorImgPath;
    }
  },
  methods: {
      getTotal() {
          let that = this;
          getTotal("").then(res => {
              if (res.success == true) {
                  this.count.createUser = res.result.sdsServer;
                  this.count.visit = res.result.sdsDatabase;
                  this.count.collection = res.result.sdsExportIng;
                  this.count.transfer = res.result.sdsExportOk;
              }
          });
      },
      timer() {
          return setInterval(()=>{
              this.getTotal();
          },5000)
      }
  },
  mounted() {
      this.getTotal();
      this.timer();
  },
  destroyed(){
        clearInterval(this.timer);
    }
};
</script>

 

子组件

<style lang="less">
    @import './styles/infor-card.less';
</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 './countUp.vue';

export default {
    name: 'inforCard',
    components: {
        countUp
    },
    watch: {
        endVal(newValue, oldValue) {
            //注意这里接收,不能直接更新prop值,需要定义变量,否则会提示有风险更新父组件
            this.endValue2 = newValue
            this.startValue2 = oldValue
            //console.log('子组件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 :id="idName">{{ 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(this.endVal);
                let endVal = res.val;
                this.unit = res.unit;
                let demo = {};
                this.demo = demo = new CountUp(this.idName, this.startVal, endVal, this.decimals, this.duration, this.options);
                if (!demo.error) {
                    demo.start();
                }
            }, this.delay);
        });
    },
    watch: {
        endVal (val) {
            let res = transformValue(val);
            let endVal = res.val;
            this.unit = res.unit;
            let demo = {};
            this.demo = demo = new CountUp(this.idName, this.startVal, endVal, this.decimals, this.duration, this.options);
            if (!demo.error) {
                demo.start();
            }
            // this.demo.update(endVal);
        }
    }
};
</script>

 


版权声明:本文为huyishero原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。