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

IT技术 javascript vue.js
2021-02-22 19:58:09

我有两个嵌套组件,从父级访问子方法的正确方法是什么?

this.$children[0].myMethod() 似乎可以解决问题,但它非常丑陋,不是吗,还有什么更好的方法:

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

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

您可以使用ref

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 所示的事件总线下面是通过将总线作为props传入来使用事件总线的另一个示例。

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/

这是正确的答案,实际上阅读了实际问题。选定的答案实际上回答了相反的问题(如何从子组件触发父级上的方法)。
2021-04-22 19:58:09
谢谢你,先生!你帮我省了很多麻烦。我正在解决一个生产问题,正在拼命寻找答案!<3
2021-04-27 19:58:09
值得一提的是,如果您使用this.$refs.,则不应动态加载子组件。
2021-05-04 19:58:09
this.$ref.ref似乎返回一个数组。所以对我来说this.$refs.ref[0].autofocus();工作
2021-05-06 19:58:09
这个答案链接的事件总线链接重定向到状态管理,在阅读@bbsimonbb评论后,它有点道理。
2021-05-08 19:58:09

VueJS中的父子通信

鉴于所有后代都可以通过 访问根 Vue 实例this.$root,父组件可以通过this.$children数组访问子组件,子组件可以通过 访问其父组件this.$parent,您的第一直觉可能是直接访问这些组件。

VueJS 文档特别出于两个很好的原因警告了这一点:

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

解决办法是使用Vue的自定义事件接口

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

  1. $on() - 允许你在你的 Vue 实例上声明一个监听器来监听事件
  2. $emit() - 允许您在同一个实例(自己)上触发事件

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

const events = new Vue({}),
    parentComponent = new Vue({
      el: '#parent',
      ready() {
        events.$on('eventGreet', () => {
          this.parentMsg = `I heard the greeting event from Child component ${++this.counter} times..`;
        });
      },
      data: {
        parentMsg: 'I am listening for an event..',
        counter: 0
      }
    }),
    childComponent = new Vue({
      el: '#child',
      methods: {
      greet: function () {
        events.$emit('eventGreet');
        this.childMsg = `I am firing greeting event ${++this.counter} times..`;
      }
    },
    data: {
      childMsg: 'I am getting ready to fire an event.',
      counter: 0
    }
  });
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/1.0.28/vue.min.js"></script>

<div id="parent">
  <h2>Parent Component</h2>
  <p>{{parentMsg}}</p>
</div>

<div id="child">
  <h2>Child Component</h2>
  <p>{{childMsg}}</p>
  <button v-on:click="greet">Greet</button>
</div>

来自原帖的答案:在 VueJS 中的组件之间进行通信

谢谢,所以我会尝试通过事件来共享我的代码!
2021-05-06 19:58:09
这有助于孩子与父母的沟通。但是有没有类似的方法可以从父母到孩子呢?例如,在我允许用户添加新子项之前,我希望验证所有现有的子项 - 验证逻辑在子项中,所以我想遍历所有这些并执行例如 validate() 方法。
2021-05-07 19:58:09
这回答了与实际提出的问题相反的问题。Desmond Lua 的回答回答了实际问题。
2021-05-08 19:58:09
复制/粘贴内容时,最好也提及来源。
2021-05-11 19:58:09
事件总线在 Chris Fritz 的 vue 反模式列表中排名第一任何可以用事件和分布式状态建模的东西都可以用全局状态和双向绑定来建模,总的来说,你会好得多。
2021-05-17 19:58:09

建议的解决方案适用于 Vue 2,但如果您最终在这里寻找 Vue 3 Composition API 解决方案,您可以在迁移时执行以下操作:

模板中的子组件,具有方法 "doSomething" :

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

使用 Vue 2:

this.$refs.childComponentRef.doSomething( );
       

使用 Vue 3 Composition Api :

    setup( )
    {
        const childComponentRef = ref( );

        childComponentRef.value.doSomething( )

        return {
           childComponentRef
        }
     }  

如果你最终在这里寻找 Vue 3 script setup

<!-- Parent -->
<template>
    <ChildComponent ref="childComponentRef" />
</template>

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

const childComponentRef = ref()

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

// Only available in Vue >= 3.1.3
// No need to import
defineExpose({
    doSomething
})
</script>

如果您的 Vue 版本是< 3.1.3,则必须使用setupfunction 并返回该doSomething函数才能在父组件中访问它。

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

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

        return { doSomething }
    }
})
</script>
您应该使用InstanceType在创建时ref所以 const childComponentRef = ref<InstanceType<typeof ChildComponent>>()
2021-04-28 19:58:09
@sanscheese 为什么?此处或问题中未使用typescript。无论如何,defineExpose() 是我错过的东西。我将它放入我的子组件后立即工作。
2021-05-13 19:58:09

当您的控件渲染受v-if. 所以,我决定采用更简单的方法。

这个想法是使用数组作为队列来发送需要调用到子组件的方法。一旦组件被挂载,它就会处理这个队列。它监视队列以执行新方法。

(从 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混合......

我发现它应该被重置为一个空数组。还有其他一些更改需要,但在这里输入太长了,所以我发布了一个答案,其中包含使其工作所需的所有更改。谢谢你给我一个起点。
2021-04-26 19:58:09
运行方法后是否应该将 childMethodsQueue 数组重置为空?
2021-05-06 19:58:09
@McGrew 感谢您发布您的答案。我认为问题与 Vue 3 的变化有关。这段代码在我们的旧代码库中仍然有效。无论如何,我相信这些功能需要在框架中,我不高兴我以这种方式使用它。这些是 Vue.js 的改进点。
2021-05-12 19:58:09