1、背景

Vue的核心是组件模块与数据驱动。正是因为这两点,我们才能开发出各式各样的灵活的小组件。通过这些组件的相互配合就能够搭建出一个完整的界面,这里面就涉及到一个配合顺序——父组件与子组件。这两个名称的区别也仅仅是前者包含了后者,两者在开发时是独立的,但是,我们在开发过程中通常会有父子组件间的数据传递,尤其是父组件把数据交给子组件,然后让子组件渲染这种操作。

Vue为我们提供这种传递的简便方法,即子组件通过props暴露出数据接口,父组件通过绑定的方式将数据传递到子组件中。如下操作:

//子组件:child.vue
<template>
  <label >{{attrs}}</label>
</template>
<script>
  export default {
    name: 'child',
    props: {
      attrs: {
        type: Object,
        default () {
          return {}
        }
      }
    },
    //从父组件传递进来的props会在mout阶段赋值,之后便不会再触发该阶段
    //beforeCreate(开始创建实例)、created(数据和方法已经初始化,但dom还没有挂载到页面上)、
    //beforeMount(挂载之间,准备挂载)、mounted(dom跟数据都成功挂载)、
    //beforeUpdate(数据更新,页面还没有同步)、updated(数据更新,页面已经同步)、
    //beforeDestroy(实例销毁前)、destroyed(实例销毁后)
    mounted () {

    })
  }
</script>
//父组件:father.vue
<template>
  <child :attrs='fatherAttrs'>
</template>

<script>
  export default {
    name: 'child',
	data(): return{
	  fatherAttrs: {aAttr: 'A', bAttr: 'B'}
	}
  }
</script>

这种方法为我们父子组件传递简化了很多,由上面我们知道父组件向子组件传递的值只会在mount阶段挂载到页面一次,之后便不再执行这个阶段。那么就存在一个问题,如果我们把fatherAttrs对象修改后,子组件的attrs对象能够接收到改变,但没有办法同步更新到页面上,即上面这种方法无法使得父组件能动态地向子组件传递数据。

那怎么办?以下提供两种解决方法

2、解决方法

2.1、data与watch辅助【推荐】

Vue内置的监听器watch是一个监听数据发生变化的处理函数,监听范围包括data函数中的数据和props函数中的数据。而我们知道data函数中的数据是与页面紧密绑定的,那么我们就可以通过监听props中的数据,如果其修改了出发watch函数,再将新值赋予data中的对象,则能实现动态更新props的对象。这是一种从子组件的角度实现的做法,具体做法如下。

//子组件:child.vue
<template>
  <!-- 注意:这里修改为data中的对象 -->
  <label >{{dataAttrs}}</label>
</template>
<script>
  export default {
    name: 'child',
    data():{
      return{
        dataAttrs: null //新增一个对象来动态更新
      }
	}
    props: {
      attrs: {
        type: Object,
        default () {
          return {}
        }
      }
    },
    mounted () {
      this.dataAttrs= this.attrs; //用props中的对象对data中的对象赋初始值
    },
    watch: { // 监听到数据然后赋值
      attrs{
        // 监听数据发生变化的处理函数
        handler(newV) {
          this.dataAttrs = JSON.parse(JSON.stringify(newV));//将监听到修改的值赋予到dataAttrs中
        },
        // 是否开启深度监听,由于我们上面props中是一个Object对象,所以需要开启深度监听功能
        deep: true
      }
    }
</script>

2.2、重建实例【适合没有实现动态更新的第三方库】

上面第一种方法能很好地处理动态更新props的情景,但它的前提是这个子组件我们是能够修改的,也就是说,这个子组件是我们自己写的,能够随意修改里面的内容。那要是我们用的是第三方的库呢?而且这个库里面也没有实现上面那种动态更新的方法,这怎么办?这就得从父组件的角度来解决了。

在上面背景的child.vue中的注释,我们知道props无法动态更新是因为组件的生命周期在mount阶段只执行一次的原因。我们一定要实现动态监听,那就需要想方设法让mount再执行一次了,这种方法就是重建子组件的实例,销毁更新前的实例,为子组件创建一个新的生命周期。

在父组件中要让子组件重新创建,最简单的指令是v-if,当为false时,该组件在dom中被移除,当为true时,该组件重新加入到dom,此时,子组件会重新创建【注意:v-if与v-show虽然都是控制组件显示与否,但v-show只是修改了display属性,使得没有前端显示而已,其实例还是存在的。所以,一般而言,v-if与v-show相比,其消耗的时间更多】。具体做法如下:

//父组件:father.vue
<template>
  <child :attrs='fatherAttrs' v-if="show">
</template>

<script>
  export default {
    name: 'child',
	data(): return{
	  fatherAttrs: {aAttr: 'A', bAttr: 'B'},
	  show: true
	},
	methods: {
	  changeAttrs(){ //触发该函数,动态更新子组件
	    this.fatherAttrs = {aAttr: 'C', bAttr: 'D'};
	    this.show= false;
        this.$nextTick(() => {
          this.show= true;
        });
	  }
	}
  }
</script>

2.3、Vuex辅助【不推荐】

原本不想把这个添加到这里面的,因为仅仅父子组件传递数据就需要用到Vuex的话,那真的是有些杀鸡用牛刀了。但是就顺手记录一下吧。具体做法相对上面比较复杂一点,建议参考以下文章:vue中使用vuex(超详细)

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐