เรียกใช้วิธีการส่วนประกอบ Vue.js จากภายนอกส่วนประกอบ


229

สมมติว่าฉันมีอินสแตนซ์ Vue หลักที่มีคอมโพเนนต์ลูก มีวิธีการเรียกใช้เมธอดที่เป็นของหนึ่งในคอมโพเนนต์เหล่านี้จากภายนอกอินสแตนซ์ Vue ทั้งหมดหรือไม่?

นี่คือตัวอย่าง:

var vm = new Vue({
  el: '#app',
  components: {
    'my-component': { 
      template: '#my-template',
      data: function() {
        return {
          count: 1,
        };
      },
      methods: {
        increaseCount: function() {
          this.count++;
        }
      }
    },
  }
});

$('#external-button').click(function()
{
  vm['my-component'].increaseCount(); // This doesn't work
});
<script src="http://vuejs.org/js/vue.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="app">
  
  <my-component></my-component>
  <br>
  <button id="external-button">External Button</button>
</div>
  
<template id="my-template">
  <div style="border: 1px solid; padding: 5px;">
  <p>A counter: {{ count }}</p>
  <button @click="increaseCount">Internal Button</button>
    </div>
</template>

ดังนั้นเมื่อฉันคลิกปุ่มภายในincreaseCount()วิธีการจะถูกผูกไว้กับเหตุการณ์การคลิกเพื่อให้ได้รับการเรียก ไม่มีทางที่จะผูกเหตุการณ์ไปที่ปุ่มภายนอกซึ่งเหตุการณ์คลิกฉันฟังกับ jQuery ดังนั้นฉันจะต้องมีวิธีการอื่น ๆ increaseCountเพื่อเรียกร้องไม่เป็น

แก้ไข

ดูเหมือนว่างานนี้:

vm.$children[0].increaseCount();

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


1
ฉันได้เพิ่มคำตอบโดยใช้ mxins หากคุณต้องการลอง ในความคิดของฉันฉันชอบที่จะติดตั้งแอพด้วยวิธีนี้
Omar Tanti

คำตอบ:


275

ในที่สุดฉันเลือกที่จะใช้ วูrefสั่ง สิ่งนี้อนุญาตให้คอมโพเนนต์ถูกอ้างอิงจากพาเรนต์สำหรับการเข้าถึงโดยตรง

เช่น

มีการลงทะเบียน compenent ในอินสแตนซ์หลักของฉัน:

var vm = new Vue({
    el: '#app',
    components: { 'my-component': myComponent }
});

แสดงส่วนประกอบใน template / html ด้วยการอ้างอิง:

<my-component ref="foo"></my-component>

ตอนนี้ที่อื่นฉันสามารถเข้าถึงองค์ประกอบภายนอก

<script>
vm.$refs.foo.doSomething(); //assuming my component has a doSomething() method
</script>

ดูซอนี้สำหรับตัวอย่าง: https://jsfiddle.net/xmqgnbu3/1/

(ตัวอย่างเก่าโดยใช้ Vue 1: https://jsfiddle.net/6v7y6msr/ )


6
นั่นเป็นเพราะคุณอาจไม่ได้กำหนดไว้ ดูซอที่เชื่อมโยงกัน
harryg

29
หากคุณใช้ webpack คุณจะไม่สามารถเข้าถึงได้vmเนื่องจากเป็นไปตามขอบเขตของโมดูล คุณสามารถทำสิ่งที่ชอบในของคุณwindow.app = vm main.jsที่มา: forum.vuejs.org/t/how-to-access-vue-from-chrome-console/3606
tw airball

1
วิธีนี้สามารถพิจารณาตามปกติได้หรือไม่? หรือว่าเป็นแฮ็ค
CENT1PEDE

3
มีคำจำกัดความที่เป็นทางการของสิ่งที่แฮ็คเทียบกับการเข้ารหัส "ปกติ" แต่แทนที่จะเรียกวิธีนี้ว่าแฮ็ค (หรือมองหาวิธี "แฮ็คน้อย" เพื่อให้ได้มาในสิ่งเดียวกัน) น่าจะดีกว่าถ้าถามว่าทำไมคุณต้องทำ นี้. ในหลายกรณีอาจใช้ระบบอีเวนต์ของ Vue เพื่อกระตุ้นการทำงานขององค์ประกอบภายนอกหรืออาจถามว่าทำไมคุณถึงเรียกใช้ส่วนประกอบจากภายนอก
harryg

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

37

ตั้งแต่ Vue2 สิ่งนี้ใช้:

var bus = new Vue()

// ในวิธีการของ component A

bus.$emit('id-selected', 1)

// ใน hook ของ component B ที่สร้างขึ้น

bus.$on('id-selected', function (id) {

  // ...
})

ดูที่นี่สำหรับเอกสาร Vue และที่นี่คือรายละเอียดเพิ่มเติมเกี่ยวกับวิธีการตั้งค่าบัสเหตุการณ์นี้อย่างแน่นอน

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


1
สั้นและหวาน! ถ้าคุณไม่ชอบทั่วโลกbusตัวแปรคุณสามารถไปขั้นตอนต่อไปและฉีดรถบัสเข้าองค์ประกอบของคุณโดยใช้อุปกรณ์ประกอบฉาก ฉันค่อนข้างใหม่ในการ vue ดังนั้นฉันไม่สามารถมั่นใจได้ว่านี่เป็นสำนวน
byxor

31

คุณสามารถใช้ระบบเหตุการณ์ Vue

vm.$broadcast('event-name', args)

และ

 vm.$on('event-name', function())

นี่คือซอ: http://jsfiddle.net/hfalucas/wc1gg5v4/59/


5
@GusDeCooL ตัวอย่างได้รับการแก้ไข ไม่ใช่ว่าหลังจาก Vuejs 2.0 มีการใช้วิธีการบางอย่างแล้ว
Helder Lucas

1
ใช้งานได้ดีหากมีเพียง 1 องค์ประกอบของส่วนประกอบ แต่ถ้ามีหลายกรณีใช้งานได้ดีกว่าใช้ $ refs.component.method ()
Koronos

22

คำตอบที่ยอมรับ (แตกต่างกันเล็กน้อย) ต่างกันเล็กน้อย:

มีส่วนประกอบที่ลงทะเบียนในอินสแตนซ์หลัก:

export default {
    components: { 'my-component': myComponent }
}

แสดงส่วนประกอบใน template / html ด้วยการอ้างอิง:

<my-component ref="foo"></my-component>

เข้าถึงวิธีการส่วนประกอบ:

<script>
    this.$refs.foo.doSomething();
</script>

22

คุณสามารถตั้งค่าการอ้างอิงสำหรับองค์ประกอบเด็กจากนั้นผู้ปกครองสามารถโทรผ่าน $ refs:

เพิ่มการอ้างอิงไปยังองค์ประกอบย่อย:

<my-component ref="childref"></my-component>

เพิ่มกิจกรรมการคลิกให้กับผู้ปกครอง:

<button id="external-button" @click="$refs.childref.increaseCount()">External Button</button>

var vm = new Vue({
  el: '#app',
  components: {
    'my-component': { 
      template: '#my-template',
      data: function() {
        return {
          count: 1,
        };
      },
      methods: {
        increaseCount: function() {
          this.count++;
        }
      }
    },
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  
  <my-component ref="childref"></my-component>
  <button id="external-button" @click="$refs.childref.increaseCount()">External Button</button>
</div>
  
<template id="my-template">
  <div style="border: 1px solid; padding: 2px;" ref="childref">
    <p>A counter: {{ count }}</p>
    <button @click="increaseCount">Internal Button</button>
  </div>
</template>


2
ทางออกที่สะอาดที่สุด
grreeenn

คำตอบที่ดี ฉันแค่จะแก้ไข html ให้เล็กลงในแนวตั้งนิดหน่อย ขณะนี้ฉันเห็นเฉพาะ 'ปุ่มภายใน' เมื่อฉันเรียกใช้ซึ่งอาจทำให้เกิดความสับสน
Jesse Reza Khorasanee

8

สมมติว่าคุณมีchild_method()องค์ประกอบย่อย:

export default {
    methods: {
        child_method () {
            console.log('I got clicked')
        }
    }
}

ตอนนี้คุณต้องการดำเนินการchild_methodจากองค์ประกอบหลัก:

<template>
    <div>
        <button @click="exec">Execute child component</button>
        <child-cmp ref="child"></child_cmp> <!-- note the ref="child" here -->
    </div>
</template>

export default {
    methods: {
        exec () { //accessing the child component instance through $refs
            this.$refs.child.child_method() //execute the method belongs to the child component
        }
    }
}

หากคุณต้องการดำเนินการวิธีการส่วนประกอบหลักจากองค์ประกอบลูก:

this.$parent.name_of_method()

หมายเหตุ: ไม่แนะนำให้เข้าถึงส่วนย่อยและองค์ประกอบหลักเช่นนี้

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

หากคุณต้องการการสื่อสารระหว่างส่วนประกอบให้ใช้vuexหรือevent busอย่างแน่นอน

โปรดอ่านบทความที่มีประโยชน์มากนี้



ใช่คุณสามารถ แต่ไม่ได้รับการพิจารณาว่าเป็น 'แนวปฏิบัติที่ดีที่สุด': ลงสู่คุณสมบัติการใช้ลูกขึ้นไปสู่กิจกรรมการใช้งานหลัก หากต้องการครอบคลุม 'ด้านข้าง' ให้ใช้เหตุการณ์ที่กำหนดเองหรือเช่น vuex ดูบทความที่ดีนี้สำหรับข้อมูลเพิ่มเติม
musicformellons

ใช่ไม่แนะนำให้ทำเช่นนั้น
roli roli

3

นี่เป็นวิธีที่ง่ายในการเข้าถึงวิธีการของส่วนประกอบจากส่วนประกอบอื่น ๆ

// This is external shared (reusable) component, so you can call its methods from other components

export default {
   name: 'SharedBase',
   methods: {
      fetchLocalData: function(module, page){
          // .....fetches some data
          return { jsonData }
      }
   }
}

// This is your component where you can call SharedBased component's method(s)
import SharedBase from '[your path to component]';
var sections = [];

export default {
   name: 'History',
   created: function(){
       this.sections = SharedBase.methods['fetchLocalData']('intro', 'history');
   }
}


1

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

import myComponent from './MyComponent'

แล้วเรียกวิธีการใด ๆ ของ MyCompenent

myComponent.methods.doSomething()

สิ่งนี้ไม่อนุญาตให้คุณเข้าถึงข้อมูลใด ๆ ในส่วนประกอบของคุณ หากคุณ doSomethingกำลังใช้สิ่งใดจากข้อมูลวิธีนี้จะไร้ประโยชน์
cyboashu

-7

ฉันใช้วิธีแก้ปัญหาที่ง่ายมาก ฉันได้รวมองค์ประกอบ HTML ที่เรียกใช้วิธีการในองค์ประกอบ Vue ของฉันที่ฉันเลือกใช้ Vanilla JS และฉันเรียกใช้การคลิก!

ใน Vue Component ฉันได้รวมสิ่งต่อไปนี้ไว้:

<span data-id="btnReload" @click="fetchTaskList()"><i class="fa fa-refresh"></i></span>

ที่ฉันใช้โดยใช้ Vanilla JS:

const btnReload = document.querySelector('[data-id="btnReload"]');
btnReload.click();                

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