วิธีเข้าถึงเมธอดลูกจากพาเรนต์ใน vue.js


96

ฉันมีสององค์ประกอบที่ซ้อนกันวิธีใดที่เหมาะสมในการเข้าถึงเมธอดลูกจากพาเรนต์?

this.$children[0].myMethod() ดูเหมือนจะทำเคล็ดลับ แต่มันก็ค่อนข้างน่าเกลียดไม่ใช่เหรอมีอะไรจะดีไปกว่านี้:

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

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

ก่อนอื่นให้ถามตัวเองว่าคุณจำเป็นต้องทำจริงๆไหม หากสถานะเพจทั้งหมดของคุณอยู่ในร้านค้าอย่างที่ควรจะเป็นก็ไม่จำเป็นต้องมีการสื่อสารระหว่างผู้ปกครองกับเด็ก
bbsimonbb

7
@bbsimonbb รัฐแตกต่างจากเหตุการณ์ โดยเฉพาะเกี่ยวกับการทริกเกอร์เหตุการณ์ย่อยจากผู้ปกครอง คุณยังสามารถทำอะไรก็ได้ที่คุณกำลังใช้ Vuex โดยส่ง prop downstream แต่สิ่งนี้ต้องการให้ส่วนประกอบย่อยดู prop / store สำหรับการเปลี่ยนแปลงเพื่อให้คุณเลียนแบบ RPC ได้อย่างมีประสิทธิภาพด้วยการเปลี่ยนแปลงข้อมูลซึ่งผิดธรรมดาเมื่อทั้งหมดที่คุณต้องการคือ ทริกเกอร์การกระทำในคอมโพเนนต์
Bojan Markovic

คำตอบ:


252

คุณสามารถใช้อ้างอิง

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 },
})

หากคุณไม่ชอบการมีเพศสัมพันธ์แบบแน่นคุณสามารถใช้Event Busดังที่แสดงโดย @Yosvel Quintero ด้านล่างนี้เป็นอีกตัวอย่างหนึ่งของการใช้อีเว้นท์บัสโดยส่งในบัสเป็นอุปกรณ์ประกอบฉาก

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/


39
นี่คือคำตอบที่ถูกต้องที่อ่านคำถามจริง คำตอบที่เลือกตอบคำถามตรงข้ามจริงๆ (วิธีเรียกใช้เมธอดบนพาเรนต์จากคอมโพเนนต์ย่อย)
Bojan Markovic

1
ลิงก์สำหรับEvent Busที่คำตอบนี้กำลังเชื่อมโยงกำลังเปลี่ยนเส้นทางไปยังState Managementหลังจากอ่านความคิดเห็นของ @bbsimonbb แล้วมันก็สมเหตุสมผลดี
Eido95

2
ควรกล่าวถึงหากคุณใช้this.$refs.คุณไม่ควรโหลดองค์ประกอบย่อยแบบไดนามิก
1_bug

ขอบคุณครับ! คุณช่วยฉันได้มากปัญหา ฉันกำลังแก้ไขปัญหาการผลิตและกำลังมองหาคำตอบอย่างสิ้นหวัง! <3
Osama Ibrahim

this.$ref.refดูเหมือนว่าจะส่งคืนอาร์เรย์ สำหรับฉันก็เลยthis.$refs.ref[0].autofocus();ทำงาน
Jozef Plata

27

การสื่อสารระหว่างผู้ปกครองและเด็กใน VueJS

เนื่องจากอินสแตนซ์ root 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


1
ขอบคุณดังนั้นฉันจะลองรวมรหัสของฉันผ่านกิจกรรม!
al3x

5
เมื่อคัดลอก / วางสิ่งต่างๆควรพูดถึงแหล่งที่มาด้วย
Mihai Vilcu

17
สิ่งนี้มีประโยชน์ในการสื่อสารจากเด็กถึงผู้ปกครอง แต่มีวิธีคล้ายกันในการทำจากพ่อแม่สู่ลูกหรือไม่? เช่นก่อนที่ฉันจะอนุญาตให้ผู้ใช้เพิ่มลูกใหม่ฉันต้องการให้คนที่มีอยู่ทั้งหมดได้รับการตรวจสอบความถูกต้อง - ตรรกะการตรวจสอบอยู่ในลูกดังนั้นฉันต้องการที่จะทำตามทั้งหมดและดำเนินการเช่นวิธีการ validate ()
Mateusz Bartkowiak

67
สิ่งนี้ตอบคำถามตรงข้ามกับสิ่งที่ถามจริง คำตอบของ Desmond Lua ตอบคำถามที่แท้จริง
Bojan Markovic

4
รถบัสที่จัดกิจกรรมเป็นอันดับ 1 ในรายการคริสฟริตซ์ของ antipatterns สิ่งใดก็ตามที่สามารถจำลองได้ด้วยเหตุการณ์และสถานะการกระจายสามารถสร้างแบบจำลองด้วยสถานะสากลและการผูกแบบสองทางและโดยทั่วไปคุณจะดีขึ้นมาก
bbsimonbb

1

Ref และเหตุการณ์รถบัสทั้งสองมีปัญหาเมื่อการควบคุมของคุณ Render 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ไปมิกซ์อิน ...


0

ในการสื่อสารองค์ประกอบลูกกับองค์ประกอบลูกอื่นฉันได้สร้างวิธีการในผู้ปกครองซึ่งเรียกวิธีการในลูกด้วย

this.$refs.childMethod()

และจากเด็กอีกคนฉันเรียกว่าวิธีรูท:

this.$root.theRootMethod()

มันได้ผลสำหรับฉัน


คำตอบนี้ไม่มีคำอธิบายมากนัก
vsync
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.