在v-if中渲染子组件时,子组件的Vue调用功能

时间:2019-07-22 10:50:17

标签: javascript vue.js vue-component

我想调用子组件的方法。
我认为开发人员使用$nextTick函数来处理所有子组件呈现后的数据。
但是当通过v-if指令进行渲染时,如何调用子组件的方法。
这是example

var comp = Vue.component('child', {
	data:function(){
  	return {
    	
    }
  },
  template:`
  	<div class="child">
    	I'm a child
    </div>
  `,
  methods:{
  	callFunction:function(){
    	console.log("I'm called");
    }
  }
});

var vm = new Vue({
	el:'#app',
  data:{
  	if_child:false
  },  
  methods:{
  	showChild(){
    	this.if_child = !this.if_child;
      //Calling child's function
      this.$refs.child.callFunction();
    }
  }
})
.child{
  display:inline-block;
  padding:10px;
  background:#eaeaea;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <div v-if="if_child">
    <child ref="child" class="child"></child>  
  </div>
  <button type="button" @click="showChild">
    Toggle Child
  </button>
</div>

当我尝试调用callFunction()中子组件的方法showChild()时,将引发错误。

  

未捕获的TypeError:无法读取未定义的属性'callFunction'

我认为原因是因为它在呈现子组件之前调用了子组件的功能。
我该如何解决这个问题? 谢谢。

2 个答案:

答案 0 :(得分:2)

为什么不起作用?

因为当您的v-if位于false上时,您的子组件不存在。 Vue尚未创建它,因此您的ref子级仍未定义,这意味着callFunction不会被执行(undefined

如何使用Vue.nextTick API?

我尝试在代码上实现它(我尝试了同步和异步语法),但是它仅在第一次尝试时起作用,然后子项ref再次变为undefined ...我认为这是因为该组件被销毁,因此“ ref”将为“ undefined”

我该如何解决?

我发现了两种可以解决您的问题的方法:

1-通过在您的孩子上使用v-show而不是v-if ...,这将使您的孩子始终可用(总是渲染,因此您的ref总是会被定义),并带有{ {1}}处于错误状态; <​​/ p>

2-但是,如果您坚持使用display : none,则可以添加另一个变量,该变量将在DOM完成呈现时被更改(使用nextTick API)...您的子组件将监视该变量并执行该函数在此之上……这是您可以做到的:

v-if
Vue.component('child', {
  props: ['exe'],
  watch: {
    exe() {
      this.callFunction()
    }
  },
  template: `
  	<div class="child">
    	I'm a child
    </div>
  `,
  methods: {
    callFunction: function() {
      console.log("I'm called");
    }
  }
});

var vm = new Vue({
  el: '#app',
  data: {
    if_child: false,
    executeTheChildFunction: false,
  },
  methods: {
    showChild() {
      this.if_child = !this.if_child;
      //Calling child's function
      this.$nextTick(function() {
        this.executeTheChildFunction = !this.executeTheChildFunction;
      })
    }
  }
})
.child {
  display: inline-block;
  padding: 10px;
  background: #eaeaea;
}

答案 1 :(得分:2)

如问题中所述,$nextTick是这里的解决方案。

Vue一起批量渲染。当您更改反应性数据(例如if_child)时,它不会立即导致任何渲染。而是将组件添加到需要渲染的组件列表中。完成所有数据更改后,Vue将呈现列表中的所有组件。

有两个原因。首先,渲染非常昂贵。其次,如果您正在更新数据,则数据可能处于不一致状态,无法正确呈现。

“渲染”这个名称有点误导。这听起来有点像画画。但是,它还包括创建和销毁子组件之类的事情。

$refs会在组件渲染后立即更新。这一切都发生在下一个刻度线的开始。为了等待,我们使用$nextTick

Vue.component('child', {
  template: `
    <div class="child">
      I'm a child
    </div>
  `,
  
  methods: {
    callFunction () {
      console.log("I'm called");
    }
  }
});

var vm = new Vue({
  el: '#app',
  
  data: {
    if_child: false
  },
  
  methods: {
    showChild () {
      this.if_child = !this.if_child;
      
      this.$nextTick(() => {
        const child = this.$refs.child;
        
        if (child) {
          child.callFunction();
        }
      });
    }
  }
});
.child {
  display: inline-block;
  padding: 10px;
  background: #eaeaea;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <div v-if="if_child">
    <child ref="child" class="child"></child>  
  </div>
  <button type="button" @click="showChild">
    Toggle Child
  </button>
</div>

这是关键部分:

showChild () {
  this.if_child = !this.if_child;

  this.$nextTick(() => {
    const child = this.$refs.child;

    if (child) {
      child.callFunction();
    }
  });
}

您可能想知道为什么它需要if (child) {。这是因为该按钮可切换if_child的值。方法名称showChild实际上是误导的。第一次单击该按钮将创建该子项,但是下次单击该子项将被销毁。

如果您没有将回调传递给$nextTick,它将返回一个Promise。如果愿意,可以将其与async / await一起使用:

async showChild () {
  this.if_child = !this.if_child;

  await this.$nextTick();

  const child = this.$refs.child;

  if (child) {
    child.callFunction();
  }
}