การสื่อสารระหว่างส่วนประกอบพี่น้องใน VueJs 2.0


114

ภาพรวม

ใน Vue.js 2.x, model.syncจะเลิก

ดังนั้นวิธีที่เหมาะสมในการสื่อสารระหว่างส่วนประกอบพี่น้องในVue.js 2.xคืออะไร?


พื้นหลัง

ตามที่ผมเข้าใจ Vue 2.x วิธีที่แนะนำสำหรับพี่น้องสื่อสารคือการใช้ร้านค้าหรือรถบัสเหตุการณ์

ตามที่Evan (ผู้สร้าง Vue):

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

หากชิ้นส่วนของข้อมูลความต้องการที่จะใช้ร่วมกันโดยองค์ประกอบหลายชอบ ร้านค้าทั่วโลกหรือVuex

[ ลิงก์ไปยังการสนทนา ]

และ:

.onceและ.syncเลิกใช้แล้ว ตอนนี้อุปกรณ์ประกอบฉากจะลงทางเดียวเสมอ ในการสร้างผลข้างเคียงในขอบเขตหลักส่วนประกอบจำเป็นต้องemitสร้างเหตุการณ์อย่างชัดเจนแทนที่จะอาศัยการผูกโดยนัย

ดังนั้นอีวานแสดงให้เห็นการใช้และ$emit()$on()


ความกังวล

สิ่งที่ฉันกังวลคือ:

  • แต่ละคนstoreและeventมีการมองเห็นทั่วโลก (ถูกต้องฉันหากฉันผิด);
  • เป็นการสิ้นเปลืองเกินไปที่จะสร้างร้านค้าใหม่สำหรับการสื่อสารเล็กน้อย

สิ่งที่ฉันต้องการคือขอบเขต eventsหรือstoresการมองเห็นสำหรับส่วนประกอบพี่น้อง (หรือบางทีฉันอาจไม่เข้าใจแนวคิดข้างต้น)


คำถาม

ดังนั้นวิธีที่ถูกต้องในการสื่อสารระหว่างส่วนประกอบพี่น้องคืออะไร?


2
$emitรวมกับที่จะเลียนแบบv-model .syncฉันคิดว่าคุณควรไปทาง Vuex
eltonkamami

3
ดังนั้นฉันจึงได้พิจารณาความกังวลเดียวกัน วิธีแก้ปัญหาของฉันคือการใช้ตัวปล่อยเหตุการณ์กับช่องออกอากาศที่เทียบเท่ากับ 'ขอบเขต' นั่นคือการตั้งค่าเด็ก / ผู้ปกครองและพี่น้องใช้ช่องทางเดียวกันในการสื่อสาร ในกรณีของฉันฉันใช้ไลบรารีวิทยุradio.uxder.comเพราะมันเป็นรหัสเพียงไม่กี่บรรทัดและกันกระสุน แต่หลายคนจะเลือกโหนด EventEmitter
แอพ Tremendus

คำตอบ:


85

ด้วย Vue 2.0 ผมใช้กลไก eventHub ที่แสดงให้เห็นในเอกสาร

  1. กำหนดฮับเหตุการณ์ส่วนกลาง

    const eventHub = new Vue() // Single event hub
    
    // Distribute to components using global mixin
    Vue.mixin({
        data: function () {
            return {
                eventHub: eventHub
            }
        }
    })
    
  2. ตอนนี้ในองค์ประกอบของคุณคุณสามารถปล่อยเหตุการณ์ด้วย

    this.eventHub.$emit('update', data)
  3. และเพื่อฟังคุณทำ

    this.eventHub.$on('update', data => {
    // do your thing
    })
    

อัปเดต โปรดดูคำตอบโดย@alexซึ่งอธิบายวิธีแก้ปัญหาที่ง่ายกว่า


3
โปรดทราบ: จับตาดู Global Mixins และพยายามหลีกเลี่ยงเมื่อใดก็ตามที่เป็นไปได้ตามลิงค์นี้vuejs.org/v2/guide/mixins.html#Global-Mixin ซึ่งอาจส่งผลกระทบแม้กระทั่งส่วนประกอบของบุคคลที่สาม
Vini.g ดู

6
วิธีแก้ปัญหาที่ง่ายกว่ามากคือใช้สิ่งที่ @Alex อธิบาย - this.$root.$emit()และthis.$root.$on()
Webnet

5
สำหรับการอ้างอิงในอนาคตโปรดอย่าอัปเดตคำตอบของคุณด้วยคำตอบของผู้อื่น (แม้ว่าคุณจะคิดว่าดีกว่าและคุณอ้างอิงก็ตาม) เชื่อมโยงไปยังคำตอบอื่นหรือแม้กระทั่งขอให้ OP ยอมรับคำตอบอื่นหากคุณคิดว่าควร - แต่การคัดลอกคำตอบเป็นของคุณเองนั้นเป็นรูปแบบที่ไม่ดีและกีดกันผู้ใช้จากการให้เครดิตเมื่อถึงกำหนดเนื่องจากอาจเพิ่มคะแนนเฉพาะของคุณ ตอบเท่านั้น กระตุ้นให้พวกเขาไปที่ (และโหวตให้คะแนน) คำตอบที่คุณอ้างอิงโดยไม่รวมคำตอบนั้นไว้ในตัวของคุณเอง
GrayedFox

4
ขอบคุณสำหรับข้อเสนอแนะที่มีค่า @GrayedFox อัปเดตคำตอบของฉันตามนี้
kakoni

2
โปรดทราบว่าระบบจะไม่รองรับโซลูชันนี้ใน Vue 3 อีกต่อไปดู stackoverflow.com/a/60895076/752916
AlexMA

148

คุณสามารถทำให้สั้นลงและใช้อินสแตนซ์รูท Vueเป็นศูนย์กลางเหตุการณ์ส่วนกลาง:

ส่วนประกอบที่ 1:

this.$root.$emit('eventing', data);

ส่วนประกอบ 2:

mounted() {
    this.$root.$on('eventing', data => {
        console.log(data);
    });
}

2
สิ่งนี้ได้ผลดีกว่าการกำหนดฮับเหตุการณ์เพิ่มเติมและเชื่อมต่อกับผู้บริโภคเหตุการณ์ใด ๆ
schad

2
ฉันเป็นแฟนตัวยงของโซลูชันนี้เพราะฉันไม่ชอบงานที่มีขอบเขต อย่างไรก็ตามฉันไม่ได้อยู่กับ VueJS ทุกวันดังนั้นฉันจึงสงสัยว่ามีใครเห็นปัญหาเกี่ยวกับแนวทางนี้หรือไม่
Webnet

2
คำตอบที่ง่ายที่สุด
Vikash Gupta

1
ดีสั้นและใช้งานง่ายเข้าใจง่ายเช่นกัน
ดา

1
หากคุณต้องการสื่อสารกับพี่น้องโดยตรงเท่านั้นให้ใช้ $ parent แทน $ root
Malkev

47

ประเภทการสื่อสาร

เมื่อออกแบบแอปพลิเคชัน Vue (หรือในความเป็นจริงแอปพลิเคชันที่ใช้ส่วนประกอบใด ๆ ) มีประเภทการสื่อสารที่แตกต่างกันขึ้นอยู่กับความกังวลที่เรากำลังเผชิญอยู่และมีช่องทางการสื่อสารของตนเอง

ตรรกะทางธุรกิจ:หมายถึงทุกสิ่งที่เฉพาะเจาะจงสำหรับแอปของคุณและเป้าหมายของแอป

ตรรกะการนำเสนอ:สิ่งใดก็ตามที่ผู้ใช้โต้ตอบหรือเป็นผลมาจากการโต้ตอบจากผู้ใช้

ข้อกังวลทั้งสองนี้เกี่ยวข้องกับการสื่อสารประเภทนี้:

  • สถานะแอปพลิเคชัน
  • พ่อแม่ลูก
  • ผู้ปกครองเด็ก
  • พี่น้อง

แต่ละประเภทควรใช้ช่องทางการสื่อสารที่เหมาะสม


ช่องทางการสื่อสาร

ช่องเป็นคำศัพท์หลวม ๆ ที่ฉันจะใช้เพื่ออ้างถึงการใช้งานที่เป็นรูปธรรมเพื่อแลกเปลี่ยนข้อมูลรอบ ๆ แอป Vue

อุปกรณ์ประกอบฉาก: ตรรกะการนำเสนอของผู้ปกครองและเด็ก

ช่องทางการสื่อสารที่ง่ายที่สุดใน Vue สำหรับการสื่อสารระหว่างพ่อแม่และลูกโดยตรง ส่วนใหญ่ควรใช้เพื่อส่งผ่านข้อมูลที่เกี่ยวข้องกับตรรกะการนำเสนอหรือชุดข้อมูลที่ จำกัด ลงตามลำดับชั้น

Refs and method: Presentation anti-pattern

เมื่อไม่สมเหตุสมผลที่จะใช้ไม้ค้ำยันเพื่อให้เด็กจัดการกับเหตุการณ์จากผู้ปกครองการตั้งค่าrefองค์ประกอบลูกและการเรียกใช้วิธีการนั้นก็ใช้ได้

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

เหตุการณ์: ตรรกะการนำเสนอของเด็กและผู้ปกครอง

$emitและ$on. ช่องทางการสื่อสารที่ง่ายที่สุดสำหรับการสื่อสารกับเด็กและผู้ปกครองโดยตรง อีกครั้งควรใช้สำหรับตรรกะการนำเสนอ

รถบัสเหตุการณ์

คำตอบส่วนใหญ่ให้ทางเลือกที่ดีสำหรับบัสเหตุการณ์ซึ่งเป็นหนึ่งในช่องทางการสื่อสารที่มีให้สำหรับส่วนประกอบที่อยู่ห่างไกลหรืออะไรก็ได้

สิ่งนี้จะมีประโยชน์เมื่อส่งอุปกรณ์ประกอบฉากไปทั่วสถานที่ตั้งแต่ระยะไกลขึ้นไปจนถึงส่วนประกอบลูกที่ซ้อนกันอย่างลึกซึ้งโดยแทบไม่ต้องใช้ส่วนประกอบอื่นใดเลย ใช้เท่าที่จำเป็นสำหรับข้อมูลที่เลือกอย่างระมัดระวัง

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

ต่อไปนี้แสดงให้เห็นว่าความผิดพลาดง่ายๆนำไปสู่การรั่วไหลโดยที่Itemส่วนประกอบยังคงทริกเกอร์แม้ว่าจะถูกลบออกจาก DOM ก็ตาม

อย่าลืมเอาตัวฟังออกในdestroyedตะขอเกี่ยวกับวงจรชีวิต

ร้านค้าส่วนกลาง (ตรรกะทางธุรกิจ)

Vuexเป็นวิธีที่จะไปกับ Vue สำหรับการจัดการของรัฐ มีมากกว่าแค่งานอีเวนต์และพร้อมสำหรับการใช้งานเต็มรูปแบบ

และตอนนี้คุณถามว่า :

[S] ฉันควรสร้างร้านค้าของ vuex สำหรับการสื่อสารย่อย ๆ หรือไม่?

มันส่องแสงจริงๆเมื่อ:

  • จัดการกับตรรกะทางธุรกิจของคุณ
  • การสื่อสารกับแบ็กเอนด์ (หรือชั้นการคงอยู่ของข้อมูลเช่นที่เก็บข้อมูลในเครื่อง)

ดังนั้นคอมโพเนนต์ของคุณจึงสามารถมุ่งเน้นไปที่สิ่งที่ควรจะเป็นได้อย่างแท้จริงการจัดการอินเทอร์เฟซผู้ใช้

ไม่ได้หมายความว่าคุณไม่สามารถใช้มันสำหรับตรรกะคอมโพเนนต์ แต่ฉันจะกำหนดขอบเขตตรรกะนั้นให้กับโมดูล Vuex ที่มีการเนมสเปซด้วยสถานะ UI ส่วนกลางที่จำเป็นเท่านั้น

เพื่อหลีกเลี่ยงการจัดการกับทุกสิ่งที่ยุ่งเหยิงในระดับโลกควรแยกร้านค้าออกเป็นโมดูลหลาย ๆ เนมสเปซ


ประเภทส่วนประกอบ

ในการจัดเตรียมการสื่อสารเหล่านี้ทั้งหมดและเพื่อความสะดวกในการใช้งานซ้ำเราควรคิดว่าส่วนประกอบเป็นสองประเภทที่แตกต่างกัน

  • คอนเทนเนอร์เฉพาะของแอป
  • ส่วนประกอบทั่วไป

อีกครั้งไม่ได้หมายความว่าควรใช้ส่วนประกอบทั่วไปซ้ำหรือไม่สามารถใช้คอนเทนเนอร์เฉพาะของแอปซ้ำได้ แต่มีความรับผิดชอบที่แตกต่างกัน

คอนเทนเนอร์เฉพาะของแอป

สิ่งเหล่านี้เป็นเพียงส่วนประกอบ Vue ง่ายๆที่ห่อส่วนประกอบ Vue อื่น ๆ (คอนเทนเนอร์ทั่วไปหรือคอนเทนเนอร์เฉพาะแอปอื่น ๆ ) นี่คือจุดที่การสื่อสารของร้านค้า Vuex ควรเกิดขึ้นและคอนเทนเนอร์นี้ควรสื่อสารผ่านวิธีการอื่น ๆ ที่ง่ายกว่าเช่นอุปกรณ์ประกอบฉากและผู้ฟังเหตุการณ์

คอนเทนเนอร์เหล่านี้อาจไม่มีองค์ประกอบ DOM ดั้งเดิมเลยและปล่อยให้ส่วนประกอบทั่วไปจัดการกับเทมเพลตและการโต้ตอบกับผู้ใช้

ขอบเขตeventsหรือstoresการมองเห็นอย่างใดสำหรับส่วนประกอบพี่น้อง

นี่คือจุดที่เกิดการกำหนดขอบเขต ส่วนประกอบส่วนใหญ่ไม่ทราบเกี่ยวกับร้านค้าและส่วนประกอบนี้ (ส่วนใหญ่) ควรใช้โมดูลการจัดเก็บเนมสเปซเดียวที่มีชุด จำกัดgettersและactionsใช้กับตัวช่วยการผูก Vuex ที่ให้มา

ส่วนประกอบทั่วไป

สิ่งเหล่านี้ควรได้รับข้อมูลจากอุปกรณ์ประกอบฉากทำการเปลี่ยนแปลงข้อมูลในเครื่องของตนเองและปล่อยเหตุการณ์ง่ายๆ ส่วนใหญ่แล้วพวกเขาไม่ควรรู้ว่ามีร้านค้า Vuex อยู่เลย

อาจเรียกได้ว่าเป็นคอนเทนเนอร์เนื่องจากความรับผิดชอบ แต่เพียงผู้เดียวคือการส่งไปยังส่วนประกอบ UI อื่น ๆ


การสื่อสารแบบพี่น้อง

ดังนั้นหลังจากนี้เราควรสื่อสารระหว่างองค์ประกอบพี่น้องสองคนอย่างไร?

ง่ายกว่าที่จะเข้าใจด้วยตัวอย่างเช่นสมมติว่าเรามีช่องป้อนข้อมูลและควรแชร์ข้อมูลในแอป (พี่น้องที่อยู่คนละที่ในต้นไม้) และอยู่กับแบ็กเอนด์

เริ่มต้นด้วยสถานการณ์ที่เลวร้ายที่สุดส่วนประกอบของเราจะผสมผสานการนำเสนอและตรรกะทางธุรกิจ

// MyInput.vue
<template>
    <div class="my-input">
        <label>Data</label>
        <input type="text"
            :value="value" 
            :input="onChange($event.target.value)">
    </div>
</template>
<script>
    import axios from 'axios';

    export default {
        data() {
            return {
                value: "",
            };
        },
        mounted() {
            this.$root.$on('sync', data => {
                this.value = data.myServerValue;
            });
        },
        methods: {
            onChange(value) {
                this.value = value;
                axios.post('http://example.com/api/update', {
                        myServerValue: value
                    })
                    .then((response) => {
                        this.$root.$emit('update', response.data);
                    });
            }
        }
    }
</script>

เพื่อแยกข้อกังวลทั้งสองนี้ออกเราควรรวมองค์ประกอบของเราไว้ในคอนเทนเนอร์เฉพาะของแอปและเก็บตรรกะการนำเสนอไว้ในองค์ประกอบการป้อนข้อมูลทั่วไปของเรา

องค์ประกอบการป้อนข้อมูลของเราสามารถใช้ซ้ำได้แล้วและไม่ทราบเกี่ยวกับแบ็กเอนด์หรือพี่น้อง

// MyInput.vue
// the template is the same as above
<script>
    export default {
        props: {
            initial: {
                type: String,
                default: ""
            }
        },
        data() {
            return {
                value: this.initial,
            };
        },
        methods: {
            onChange(value) {
                this.value = value;
                this.$emit('change', value);
            }
        }
    }
</script>

คอนเทนเนอร์เฉพาะแอปของเราสามารถเป็นสะพานเชื่อมระหว่างตรรกะทางธุรกิจและการสื่อสารในการนำเสนอได้แล้ว

// MyAppCard.vue
<template>
    <div class="container">
        <card-body>
            <my-input :initial="serverValue" @change="updateState"></my-input>
            <my-input :initial="otherValue" @change="updateState"></my-input>

        </card-body>
        <card-footer>
            <my-button :disabled="!serverValue || !otherValue"
                       @click="saveState"></my-button>
        </card-footer>
    </div>
</template>
<script>
    import { mapGetters, mapActions } from 'vuex';
    import { NS, ACTIONS, GETTERS } from '@/store/modules/api';
    import { MyButton, MyInput } from './components';

    export default {
        components: {
            MyInput,
            MyButton,
        },
        computed: mapGetters(NS, [
            GETTERS.serverValue,
            GETTERS.otherValue,
        ]),
        methods: mapActions(NS, [
            ACTIONS.updateState,
            ACTIONS.updateState,
        ])
    }
</script>

เนื่องจากการดำเนินการของร้านค้า Vuex จัดการกับการสื่อสารแบ็กเอนด์คอนเทนเนอร์ของเราที่นี่จึงไม่จำเป็นต้องรู้เกี่ยวกับ axios และแบ็กเอนด์


3
เห็นด้วยกับความคิดเห็นเกี่ยวกับวิธีการที่เป็น " การมีเพศสัมพันธ์แบบเดียวกับการใช้อุปกรณ์ประกอบฉาก "
ghybs

ฉันชอบคำตอบนี้ แต่คุณช่วยอธิบายรายละเอียดเกี่ยวกับ Event Bus และหมายเหตุ "ระวัง:" ได้ไหม บางทีคุณอาจยกตัวอย่างได้บ้างฉันไม่เข้าใจว่าส่วนประกอบต่างๆสามารถผูกสองครั้งได้อย่างไร
vandroid

คุณสื่อสารระหว่างองค์ประกอบหลักและองค์ประกอบลูกใหญ่อย่างไรตัวอย่างเช่นการตรวจสอบความถูกต้องของฟอร์ม โดยที่องค์ประกอบหลักคือเพจลูกคือฟอร์มและแกรนด์ชายน์คือองค์ประกอบของฟอร์มอินพุต?
Lord Zed

1
@vandroid ฉันได้สร้างตัวอย่างง่ายๆที่แสดงให้เห็นถึงการรั่วไหลเมื่อไม่ได้เอาตัวฟังออกอย่างถูกต้องเช่นเดียวกับทุกตัวอย่างในหัวข้อนี้
Emile Bergeron

@LordZed มันขึ้นอยู่กับจริงๆ แต่จากความเข้าใจของฉันเกี่ยวกับสถานการณ์ของคุณดูเหมือนว่าปัญหาในการออกแบบ ควรใช้ Vue สำหรับตรรกะในการนำเสนอเป็นส่วนใหญ่ ควรทำการตรวจสอบความถูกต้องของแบบฟอร์มที่อื่นเช่นในอินเทอร์เฟซ vanilla JS API ที่การดำเนินการของ Vuex จะเรียกด้วยข้อมูลจากแบบฟอร์ม
Emile Bergeron

10

โอเคเราสามารถสื่อสารระหว่างพี่น้องผ่านผู้ปกครองโดยใช้v-onเหตุการณ์

Parent
 |-List of items //sibling 1 - "List"
 |-Details of selected item //sibling 2 - "Details"

สมมติว่าเราต้องการปรับปรุงองค์ประกอบเมื่อเราคลิกองค์ประกอบในบางDetailsList


ในParent:

แม่แบบ:

<list v-model="listModel"
      v-on:select-item="setSelectedItem" 
></list> 
<details v-model="selectedModel"></details>

ที่นี่:

  • v-on:select-itemมันเป็นเหตุการณ์ที่จะถูกเรียกในListองค์ประกอบ (ดูด้านล่าง);
  • setSelectedItemเป็นParentวิธีการอัปเดselectedModel

JS:

//...
data () {
  return {
    listModel: ['a', 'b']
    selectedModel: null
  }
},
methods: {
  setSelectedItem (item) {
    this.selectedModel = item //here we change the Detail's model
  },
}
//...

ในList:

แม่แบบ:

<ul>
  <li v-for="i in list" 
      :value="i"
      @click="select(i, $event)">
        <span v-text="i"></span>
  </li>
</ul>

JS:

//...
data () {
  return {
    selected: null
  }
},
props: {
  list: {
    type: Array,
    required: true
  }
},
methods: {
  select (item) {
    this.selected = item
    this.$emit('select-item', item) // here we call the event we waiting for in "Parent"
  },
}
//...

ที่นี่:

  • this.$emit('select-item', item)จะส่งรายการผ่านทางselect-itemผู้ปกครองโดยตรง และผู้ปกครองจะส่งไปยังDetailsมุมมอง

5

สิ่งที่ฉันมักจะทำหากต้องการ "แฮ็ก" รูปแบบการสื่อสารปกติใน Vue โดยเฉพาะตอนนี้ที่.syncเลิกใช้แล้วคือการสร้าง EventEmitter ง่ายๆที่จัดการการสื่อสารระหว่างส่วนประกอบต่างๆ จากโครงการล่าสุดของฉัน:

import {EventEmitter} from 'events'

var Transmitter = Object.assign({}, EventEmitter.prototype, { /* ... */ })

ด้วยTransmitterวัตถุนี้คุณสามารถทำได้ในส่วนประกอบใด ๆ :

import Transmitter from './Transmitter'

var ComponentOne = Vue.extend({
  methods: {
    transmit: Transmitter.emit('update')
  }
})

และในการสร้างคอมโพเนนต์ "การรับ":

import Transmitter from './Transmitter'

var ComponentTwo = Vue.extend({
  ready: function () {
    Transmitter.on('update', this.doThingOnUpdate)
  }
})

อีกครั้งนี่คือการใช้งานเฉพาะจริงๆ อย่ายึดแอปพลิเคชันทั้งหมดของคุณเป็นรูปแบบนี้ให้ใช้สิ่งที่ชอบVuexแทน


1
ฉันใช้อยู่แล้วvuexแต่อีกครั้งฉันควรสร้างร้านค้าของ vuex สำหรับการสื่อสารเล็กน้อยแต่ละครั้งหรือไม่
Sergei Panfilov

เป็นเรื่องยากสำหรับฉันที่จะพูดด้วยข้อมูลจำนวนนี้ แต่ฉันจะบอกว่าถ้าคุณใช้vuexใช่อยู่แล้วให้ไป ใช้มัน.
Hector Lorenzo

1
อันที่จริงฉันไม่เห็นด้วยที่เราจำเป็นต้องใช้ vuex สำหรับการสื่อสารเล็กน้อยแต่ละครั้ง ...
วิกเตอร์

ไม่แน่นอนทั้งหมดขึ้นอยู่กับบริบท จริงๆแล้วคำตอบของฉันย้ายออกไปจาก vuex ในทางกลับกันฉันพบว่ายิ่งคุณใช้ vuex และแนวคิดของวัตถุสถานะกลางมากเท่าไหร่ฉันก็ยิ่งพึ่งพาการสื่อสารระหว่างวัตถุน้อยลงเท่านั้น แต่ใช่เห็นด้วยทุกอย่างขึ้นอยู่กับ
Hector Lorenzo

3

วิธีจัดการการสื่อสารระหว่างพี่น้องขึ้นอยู่กับสถานการณ์ แต่ครั้งแรกที่ผมอยากจะเน้นว่าวิธีการที่รถบัสเหตุการณ์ที่ทั่วโลกกำลังจะหายไปใน Vue 3 ดูRFCนี้ เหตุใดฉันจึงตัดสินใจเขียนคำตอบใหม่

รูปแบบบรรพบุรุษร่วมที่ต่ำที่สุด (หรือ“ LCA”)

สำหรับกรณีทั่วไปฉันขอแนะนำอย่างยิ่งให้ใช้รูปแบบบรรพบุรุษร่วมต่ำที่สุด (หรือที่เรียกว่า "data down, events up") รูปแบบนี้ง่ายต่อการอ่านใช้งานทดสอบและแก้ไขข้อบกพร่อง

โดยพื้นฐานแล้วหมายความว่าหากส่วนประกอบสองส่วนจำเป็นต้องสื่อสารกันให้กำหนดสถานะที่ใช้ร่วมกันไว้ในองค์ประกอบที่ใกล้เคียงที่สุดที่ทั้งสองมีส่วนร่วมในฐานะบรรพบุรุษ ส่งผ่านข้อมูลจากองค์ประกอบหลักไปยังองค์ประกอบลูกผ่านอุปกรณ์ประกอบฉากและส่งผ่านข้อมูลจากเด็กไปยังผู้ปกครองโดยการเปล่งเหตุการณ์ (ดูตัวอย่างนี้ที่ด้านล่างของคำตอบนี้)

สำหรับตัวอย่างที่สร้างขึ้นในแอปอีเมลหากองค์ประกอบ "ถึง" จำเป็นในการโต้ตอบกับองค์ประกอบ "เนื้อหาข้อความ" สถานะของการโต้ตอบนั้นอาจอยู่ในพาเรนต์ (อาจเป็นส่วนประกอบที่เรียกว่าemail-form) คุณอาจมีเสาในการemail-formเรียกaddresseeเพื่อให้เนื้อหาของข้อความสามารถDear {{addressee.name}}นำหน้าอีเมลโดยอัตโนมัติตามที่อยู่อีเมลของผู้รับ

LCA กลายเป็นเรื่องยุ่งยากหากการสื่อสารต้องเดินทางเป็นระยะทางไกลโดยมีส่วนประกอบของพ่อค้าคนกลางจำนวนมาก ฉันมักจะแนะนำเพื่อนร่วมงานให้มาที่บล็อกโพสต์ที่ยอดเยี่ยมนี้ (ไม่สนใจข้อเท็จจริงที่ว่าตัวอย่างของมันใช้ Ember แนวคิดของมันสามารถใช้ได้กับกรอบงาน UI จำนวนมาก)

รูปแบบคอนเทนเนอร์ข้อมูล (เช่น Vuex)

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

ตัวอย่างเช่นอาจเป็นเรื่องสมเหตุสมผลที่จะสร้างเนมสเปซแยกต่างหากสำหรับคอลเลกชันที่ซับซ้อนของคอมโพเนนต์ที่มีการเชื่อมต่อระหว่างกันเช่นส่วนประกอบปฏิทินที่มีคุณสมบัติครบถ้วน

รูปแบบการเผยแพร่ / สมัครสมาชิก (บัสเหตุการณ์)

หากรถบัสเหตุการณ์ (หรือ“เผยแพร่ / สมัครสมาชิก”) รูปแบบที่มีความเหมาะสมสำหรับความต้องการของทีมแกนวูตอนนี้ขอแนะนำให้ใช้ห้องสมุดของบุคคลที่สามเช่นนวม (ดู RFC ที่อ้างถึงในย่อหน้าที่ 1)

รายชื่อโบนัสและรหัส

นี่เป็นตัวอย่างพื้นฐานของสุดของการแก้ปัญหาร่วมกันบรรพบุรุษสำหรับการสื่อสารพี่น้องเพื่อพี่น้องที่แสดงผ่านเกมตีตุ่น

วิธีการที่ไร้เดียงสาอาจคิดว่า“ ไฝ 1 ควรบอกให้ไฝ 2 ปรากฏขึ้นหลังจากที่ถูกตี” แต่วูอุปสรรคการเรียงลำดับของวิธีนี้เพราะมันต้องการให้เราคิดในแง่ของโครงสร้างต้นไม้

นี่น่าจะเป็นสิ่งที่ดีมาก แอปพลิเคชันที่ไม่สำคัญที่โหนดสื่อสารถึงกันโดยตรงผ่านโครงสร้าง DOM จะเป็นการยากมากที่จะดีบักโดยไม่มีระบบบัญชีบางประเภท (เช่น Vuex ให้) ยิ่งไปกว่านั้นส่วนประกอบที่ใช้“ data down, events up” มักจะแสดงการมีเพศสัมพันธ์ที่ต่ำและความสามารถในการใช้งานซ้ำได้สูงซึ่งทั้งสองลักษณะที่ต้องการอย่างมากซึ่งช่วยให้แอปขนาดใหญ่มีขนาด

ในตัวอย่างนี้เมื่อตุ่นถูกตีมันจะส่งเสียงเหตุการณ์ คอมโพเนนต์ตัวจัดการเกมจะตัดสินใจว่าสถานะใหม่ของแอปคืออะไรดังนั้นตุ่นพี่น้องจึงรู้ว่าต้องทำอย่างไรโดยปริยายหลังจากที่ Vue แสดงผลอีกครั้ง เป็นตัวอย่าง "บรรพบุรุษร่วมชั้นต่ำที่สุด" ที่ค่อนข้างเล็กน้อย

Vue.component('whack-a-mole', {
  data() {
    return {
      stateOfMoles: [true, false, false],
      points: 0
    }
  },
  template: `<div>WHACK - A - MOLE!<br/>
    <a-mole :has-mole="stateOfMoles[0]" v-on:moleMashed="moleClicked(0)"/>
    <a-mole :has-mole="stateOfMoles[1]"  v-on:moleMashed="moleClicked(1)"/>
    <a-mole :has-mole="stateOfMoles[2]" v-on:moleMashed="moleClicked(2)"/>
    <p>Score: {{points}}</p>
</div>`,
  methods: {
    moleClicked(n) {
      if(this.stateOfMoles[n]) {
         this.points++;
         this.stateOfMoles[n] = false;
         this.stateOfMoles[Math.floor(Math.random() * 3)] = true;
      }   
    }
  }
})

Vue.component('a-mole', {
  props: ['hasMole'],
  template: `<button @click="$emit('moleMashed')">
      <span class="mole-button" v-if="hasMole">🐿</span><span class="mole-button" v-if="!hasMole">🕳</span>
    </button>`
})

var app = new Vue({
  el: '#app',
  data() {
    return { name: 'Vue' }
  }
})
.mole-button {
  font-size: 2em;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <whack-a-mole />
</div>

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