prop介绍

prop是单向数据流,父组件传递给子组件的数据发生改变,而子组件的页面中引用了该prop,那么按理说子组件会重新渲染,将最新的prop渲染到页面上。

能正常更新的案例

在父组件中启动定时器,每隔1s对状态数据parentNum进行加一操作。父组件中引用子组件时将parentNum作为属性传入。

下面的例子实现的效果是子组件每隔一秒都会更新。从0开始每隔一秒加1并更新在页面上。
请添加图片描述

<!--父组件-->

<template>
  <div class="parentContainer">
      我是父组件
      <child ref="mychild" :num="parentNum"></child>
  </div>
</template>

<script>
import Child from './Child.vue'
export default {
  data () {
    return {
      parentNum: 0
    }
  },
  components: {
    Child
  },
  mounted () {
    this.changeData()
  },
  methods: {
    changeData () {
      const _this = this
      setInterval(function () {
        _this.parentNum = _this.parentNum + 1
      }, 1000)
    }
  }
}
</script>

<style scoped>
.parentContainer {
    width: 500px;
    height: 500px;
    background-color: gray;
}
</style>
<!--子组件-->

<template>
  <div class="childContainer">
      我是子组件
      <div class="inner" ref="inner">{{num}}</div> <!--在模板中使用num-->
  </div>
</template>

<script>
export default {
  props: {
    num: Number
  },
}
</script>

<style scoped>
.childContainer {
    width: 200px;
    height: 200px;
    background-color: green;
    font-size: 30px;
    margin: auto;
}
.inner {
    color: red;
}
</style>

不能更新的按钮

如果我们在子组件的模板中没有使用到prop变量,而只是在子组件的方法中使用了prop变量,那么根据vue的绑定原理来说是不会触发重绘的。所以子组件中即使获取到更改后的prop但是组件不会进行重新渲染。

参考下面的例子:下面的例子是子组件中永远显示0。通过调试工具我们发现,子组件的num一直在进行自加操作,但是没有更新页面。

<!--父组件-->

<template>
  <div class="parentContainer">
      我是父组件
      <child ref="mychild" :num="parentNum"></child>
  </div>
</template>

<script>
import Child from './Child.vue'
export default {
  data () {
    return {
      parentNum: 0
    }
  },
  components: {
    Child
  },
  mounted () {
    this.changeData()
  },
  methods: {
    changeData () {
      const _this = this
      setInterval(function () {
        _this.parentNum = _this.parentNum + 1
      }, 1000)
    }
  }
}
</script>

<style scoped>
.parentContainer {
    width: 500px;
    height: 500px;
    background-color: gray;
}
</style>
<!--子组件-->

<template>
  <div class="childContainer">
      我是子组件
      <div class="inner" ref="inner"></div>
  </div>
</template>

<script>
export default {
  props: {
    num: Number
  },
  mounted () {
    this.init()
  },
  methods: {
    init () {
      this.$refs.inner.innerHTML = this.num // 在此处使用了prop 在模板中没有使用prop
      // 通过上面的语句去更新页面
    }
  }
}
</script>

<style scoped>
.childContainer {
    width: 200px;
    height: 200px;
    background-color: green;
    font-size: 30px;
    margin: auto;
}
.inner {
    color: red;
}
</style>

解决方案

通过分析上面的代码,我们发现页面不更新的根本原因是因为子组件的init方法没有被调用。所以有以下三种解决方案。

第一种方案是在子组件中监听prop的变化然后去调用方法

// 子组件

export default {
  props: {
    num: Number
  },
  mounted () {
    this.init()
  },
  watch: {
    num (val) { // 关键
      this.init() // 调用init
    }
  },
  methods: {
    init () {
      this.$refs.inner.innerHTML = this.num
    }
  }
}

第二种方案是通过nextTick结合ref在父组件中调用子组件的方法。nextTick的作用是在在下次 DOM 更新循环结束之后执行延迟回调。

// 父组件

export default {
  data () {
    return {
      parentNum: 0
    }
  },
  components: {
    Child
  },
  mounted () {
    this.changeData()
  },
  methods: {
    changeData () {
      const _this = this
      setInterval(function () {
        console.log(1)
        _this.parentNum = _this.parentNum + 1
        _this.$nextTick(() => {
          _this.$refs.mychild.init() // 调用
        })
      }, 1000)
    }
  }
}

第三种方案是在使用子组件时添加key属性

<!--父组件-->

<template>
  <div class="parentContainer">
      我是父组件
      <child :num="parentNum" ref="mychild" :key="new Date().getTime()"></child>
  </div>
</template>

参考

《Vue父组件修改了通过 props传递给子组件的数据,子组件没有及时更新》

Logo

前往低代码交流专区

更多推荐