如何从vue.js中的父方法访问子方法

zed5wv10  于 2022-12-14  发布在  Vue.js
关注(0)|答案(7)|浏览(208)

我有两个嵌套的组件,从父方法访问子方法的正确方法是什么?
this.$children[0].myMethod()看起来很管用但其实也挺难看的,不是吗,还能有什么更好的办法:

<script>
import child from './my-child'

export default {
  components: {
   child
  },
  mounted () {
    this.$children[0].myMethod()
  }
}
</script>
ylamdve6

ylamdve61#

您可以使用参考

import ChildForm from './components/ChildForm'

new Vue({
  el: '#app',
  data: {
    item: {}
  },
  template: `
  <div>
     <ChildForm :item="item" ref="form" />
     <button type="submit" @click.prevent="submit">Post</button>
  </div>
  `,
  methods: {
    submit() {
      this.$refs.form.submit()
    }
  },
  components: { ChildForm },
})

如果你不喜欢紧耦合,你可以使用事件总线,如@Yosvel Quintero所示。下面是另一个使用事件总线的例子,通过将总线作为 prop 传递。

import ChildForm from './components/ChildForm'

new Vue({
  el: '#app',
  data: {
    item: {},
    bus: new Vue(),
  },
  template: `
  <div>
     <ChildForm :item="item" :bus="bus" ref="form" />
     <button type="submit" @click.prevent="submit">Post</button>
  </div>
  `,
  methods: {
    submit() {
      this.bus.$emit('submit')
    }
  },
  components: { ChildForm },
})

组件的代码。

<template>
 ...
</template>

<script>
export default {
  name: 'NowForm',
  props: ['item', 'bus'],
  methods: {
    submit() {
        ...
    }
  },
  mounted() {
    this.bus.$on('submit', this.submit)
  },  
}
</script>

https://code.luasoftware.com/tutorials/vuejs/parent-call-child-component-method/

mw3dktmi

mw3dktmi2#

适用于Vue 2.7Vue 3.2.x

<!-- Parent -->
<script setup>
import { ref, onMounted } from 'vue'
import ChildComponent from './components/ChildComponent.vue'

const childComponentRef = ref()

onMounted(() => {
    childComponentRef.value.doSomething()
})
</script>

<template>
    <ChildComponent ref="childComponentRef" />
</template>

脚本设置

<!-- Child -->
<script setup>
const doSomething = () => {
    console.log('Im batman')
}

defineExpose({
    doSomething
})
</script>

设置功能

<!-- Child -->
<script>
import { defineComponent } from 'vue'

export default defineComponent({
    setup(props, context) {
        const doSomething = () => {
            console.log('Im batman')
        }

        context.expose({ doSomething })
    }
})
</script>
9fkzdhlc

9fkzdhlc3#

VueJS中的父子通信

假设所有后代都可以通过this.$root访问一个根Vue示例,父组件可以通过this.$children数组访问子组件,子组件可以通过this.$parent访问其父组件,那么您的第一React可能是直接访问这些组件。
VueJS文档特别警告不要这样做,原因有两个:

  • 它将父级与子级紧密耦合(反之亦然)
  • 您不能依赖父组件的状态,因为它可以被子组件修改。

解决方案是使用Vue的自定义事件接口

Vue实现的事件接口允许您在组件树中上下通信。利用自定义事件接口,您可以访问四种方法:

  1. $on()-允许您在Vue示例上声明侦听器,用于侦听事件
  2. $emit()-允许在同一示例(自身)上触发事件

使用$on()$emit()的示例:

第一个

答案摘自原帖:Communicating between components in VueJS

eeq64g8w

eeq64g8w4#

建议的解决方案适用于Vue 2,但如果您最终在此处寻找Vue 3组合API解决方案,则可以在迁移时执行以下操作:
模板中具有方法“doSomething”的子组件:

<div class="form">                                                                                                                                                        
      <child-component ref="childComponentRef" />                                                                      
</div>

使用Vue 2:

this.$refs.childComponentRef.doSomething( );

使用Vue 3组成API:

setup( )
    {
        const childComponentRef = ref( );

        childComponentRef.value.doSomething( )

        return {
           childComponentRef
        }
     }
cbeh67ev

cbeh67ev5#

当你的控件呈现受到v-if的影响时,Ref和事件总线都有问题。
这个想法是使用一个数组作为队列,将需要调用的方法发送给子组件。一旦组件被挂载,它将处理这个队列。它监视队列以执行新的方法。
(借用Desmond Lua回答中的一些代码)
父组件代码:

import ChildComponent from './components/ChildComponent'

new Vue({
  el: '#app',
  data: {
    item: {},
    childMethodsQueue: [],
  },
  template: `
  <div>
     <ChildComponent :item="item" :methods-queue="childMethodsQueue" />
     <button type="submit" @click.prevent="submit">Post</button>
  </div>
  `,
  methods: {
    submit() {
      this.childMethodsQueue.push({name: ChildComponent.methods.save.name, params: {}})
    }
  },
  components: { ChildComponent },
})

这是ChildComponent代码

<template>
 ...
</template>

<script>
export default {
  name: 'ChildComponent',
  props: {
    methodsQueue: { type: Array },
  },
  watch: {
    methodsQueue: function () {
      this.processMethodsQueue()
    },
  },
  mounted() {
    this.processMethodsQueue()
  },
  methods: {
    save() {
        console.log("Child saved...")
    },
    processMethodsQueue() {
      if (!this.methodsQueue) return
      let len = this.methodsQueue.length
      for (let i = 0; i < len; i++) {
        let method = this.methodsQueue.shift()
        this[method.name](method.params)
      }
    },
  },
}
</script>

而且还有很大的改进空间,比如将processMethodsQueue移到mixin中。

gk7wooem

gk7wooem6#

我喜欢mohghaderi的答案,但是我遇到了一些问题,所以我将使用他的示例代码来显示我需要做的修改,以便它工作。(在我自己的项目中,我使用的是Vue 3和Options API。)

mohghaderi的父组件代码以及有关我的更改的注解:

import ChildComponent from './components/ChildComponent'

new Vue({
  el: '#app',
  data: {
    item: {},
    childMethodsQueue: [],
  },
  // Note: In the template below, I added @child-methods-finished="childMethodsFinished" 
  //       as an event listener, so that we can reset the childMethodsQueue array to
  //       empty once the methods are finished.
  //       If you don't reset it, then the methods stay in there and cause problems.
  template: `
  <div>
     <ChildComponent :item="item" 
                     :methods-queue="childMethodsQueue"
                     @child-methods-finished="childMethodsFinished" />
     <button type="submit" @click.prevent="submit">Post</button>
  </div>
  `,
  methods: {
    submit() {
      this.childMethodsQueue.push({
        name: ChildComponent.methods.save.name,
        params: {}  // Note: delete the {} and put the name of your params, if you use a method that passes in params.
      })
    }
  },
  components: { ChildComponent },
})

mohghaderi的子组件代码以及有关我的更改的注解:

import { objectToString } from "@vue/shared"

export default {
    name: 'ChildComponent',
    props: {
      methodsQueue: { type: Array },
    },
    // Note:  I had to rewrite the watch option because it would not trigger.
    //        You have to add "deep, true" for arrays and objects.
    //        The function has to be called "handler" for it to work as well.
    watch: {
      methodsQueue: {
        handler() {
          this.processMethodsQueue()
        },
        deep: true,
      }
    },
    // Note:  Remove "mounted()" function if you don't want it to run on the mounted event.
    mounted() {
      this.processMethodsQueue()
    },
    methods: {
      save() {
          console.log("Child saved...")
      }, 
      processMethodsQueue() {
        if (!this.methodsQueue) return
        let len = this.methodsQueue.length

        if (!len) return  // Note:  This is required to prevent an infinite loop.
                          //        When we reset the childMethodsQueue array to empty,
                          //        it will trigger this method through the watch option,
                          //        so we need this in order to stop the cycle once we are done.

        // Note:  Instead of using ".shift()" to access an item in the array
        //        we need to use "[i]" otherwise we will get muliple calls of the method
        for (let i = 0; i < len; i++) {
          let method = this.methodsQueue[i]
          this[method.name](method.params)
        }

        // Note:  Now that we are done calling methods, we need to emit an event back to the parent
        //        so it can call it's method to reset the childMethodsQueue array to empty
        this.$emit('child-methods-finished')
      },
    },
  }
ijnw1ujt

ijnw1ujt7#

为了使一个子组件与另一个子组件进行通信,我在parent中创建了一个方法,该方法使用以下命令调用子组件中的方法:

this.$refs.childRef.childMethod()

childRef是子组件的引用,childMethod可以替换为子组件中的任何方法。
从另一个子函数调用root方法:

this.$root.theParentMethod() // It works with Bootstrap Vue
this.$parent.theParentMethod()

对我很有效。

相关问题