Giao tiếp giữa các thành phần anh chị em trong VueJs 2.0


112

Tổng quat

Trong Vue.js 2.x, model.syncsẽ không được dùng nữa .

Vì vậy, cách thích hợp để giao tiếp giữa các thành phần anh em trong Vue.js 2.x là gì?


Lý lịch

Theo tôi hiểu Vue 2.x, phương pháp ưu tiên cho giao tiếp anh chị em là sử dụng một cửa hàng hoặc một xe buýt sự kiện .

Theo Evan (tác giả của Vue):

Cũng cần nhắc lại rằng "truyền dữ liệu giữa các thành phần" nói chung là một ý tưởng tồi, bởi vì cuối cùng luồng dữ liệu trở nên không thể theo dõi và rất khó gỡ lỗi.

Nếu một phần dữ liệu cần được chia sẻ bởi nhiều thành phần, hãy ưu tiên các cửa hàng toàn cầu hoặc Vuex .

[ Liên kết đến thảo luận ]

Và:

.once.synckhông được dùng nữa. Đạo cụ hiện luôn ở một chiều. Để tạo ra các hiệu ứng phụ trong phạm vi chính, một thành phần cần phải xác định rõ ràng emitmột sự kiện thay vì dựa vào ràng buộc ngầm định.

Vì vậy, Evan đề nghị sử dụng $emit()$on().


Mối quan tâm

Điều làm tôi lo lắng là:

  • Mỗi storevà đều eventcó khả năng hiển thị toàn cầu (sửa cho tôi nếu tôi sai);
  • Quá lãng phí để tạo một cửa hàng mới cho mỗi giao tiếp nhỏ;

Những gì tôi muốn là một số phạm vi events hoặc storeskhả năng hiển thị cho các thành phần anh chị em. (Hoặc có lẽ tôi đã không hiểu ý tưởng trên.)


Câu hỏi

Vì vậy, cách chính xác để giao tiếp giữa các thành phần anh chị em là gì?


2
$emitkết hợp với v-modelđể thi đua .sync. tôi nghĩ bạn nên đi theo cách
Vuex

3
Vì vậy, tôi đã xem xét cùng một mối quan tâm. Giải pháp của tôi là sử dụng bộ phát sự kiện có kênh quảng bá tương đương với 'phạm vi' - tức là thiết lập con / cha mẹ và anh chị em sử dụng cùng một kênh để giao tiếp. Trong trường hợp của tôi, tôi sử dụng thư viện radio radio.uxder.com vì nó chỉ có một vài dòng mã và khả năng chống đạn của nó, nhưng nhiều người sẽ chọn nút EventEmitter.
Ứng dụng Tremendus vào

Câu trả lời:


83

Với Vue 2.0, tôi đang sử dụng cơ chế eventHub như được trình bày trong tài liệu .

  1. Xác định trung tâm sự kiện tập trung.

    const eventHub = new Vue() // Single event hub
    
    // Distribute to components using global mixin
    Vue.mixin({
        data: function () {
            return {
                eventHub: eventHub
            }
        }
    })
    
  2. Bây giờ trong thành phần của bạn, bạn có thể phát ra các sự kiện với

    this.eventHub.$emit('update', data)
  3. Và để lắng nghe bạn làm

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

Cập nhật Vui lòng xem câu trả lời của @alex , mô tả một giải pháp đơn giản hơn.


3
Lưu ý: hãy để mắt đến Global Mixins và cố gắng tránh chúng bất cứ khi nào có thể, vì theo liên kết này vuejs.org/v2/guide/mixins.html#Global-Mixin, chúng có thể ảnh hưởng đến cả các thành phần của bên thứ ba.
Vini.g.fer

6
Một giải pháp đơn giản hơn nhiều là sử dụng những gì @Alex mô tả - this.$root.$emit()this.$root.$on()
Webnet

5
Để tham khảo trong tương lai, vui lòng không cập nhật câu trả lời của bạn với câu trả lời của người khác (ngay cả khi bạn nghĩ nó tốt hơn và bạn tham khảo nó). Liên kết đến câu trả lời thay thế hoặc thậm chí yêu cầu OP chấp nhận câu trả lời khác nếu bạn nghĩ họ nên - nhưng việc sao chép câu trả lời của họ vào câu trả lời của riêng bạn là một hình thức xấu và không khuyến khích người dùng cung cấp tín dụng khi nó đến hạn, vì họ chỉ có thể ủng hộ chỉ trả lời. Khuyến khích họ điều hướng đến (và do đó ủng hộ) câu trả lời bạn đang tham khảo bằng cách không bao gồm câu trả lời đó của riêng bạn.
GrayedFox

4
Cảm ơn bạn vì phản hồi có giá trị @GrayedFox, đã cập nhật câu trả lời của tôi cho phù hợp.
kakoni

2
Xin lưu ý rằng giải pháp này sẽ không còn được hỗ trợ trong Vue 3. Xem stackoverflow.com/a/60895076/752916
AlexMA

145

Bạn thậm chí có thể làm cho nó ngắn hơn và sử dụng phiên bản gốc Vue làm Trung tâm sự kiện toàn cầu:

Thành phần 1:

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

Thành phần 2:

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

2
Điều này hoạt động tốt hơn việc xác định một trung tâm sự kiện bổ sung và gắn nó vào bất kỳ người tiêu dùng sự kiện nào.
schad

2
Tôi là một fan hâm mộ lớn của giải pháp này vì tôi thực sự không thích các sự kiện có phạm vi. Tuy nhiên, tôi không sử dụng VueJS mỗi ngày nên tôi tò mò không biết có ai ngoài kia gặp vấn đề với cách tiếp cận này không.
Webnet

2
Giải pháp đơn giản nhất của tất cả các câu trả lời
Vikash Gupta

1
đẹp, ngắn gọn và dễ thực hiện, dễ hiểu cũng
nada

1
Nếu bạn chỉ muốn giao tiếp độc quyền với anh chị em ruột trực tiếp, hãy sử dụng $ parent thay vì $ root
Malkev

47

Các loại giao tiếp

Khi thiết kế một ứng dụng Vue (hoặc trên thực tế, bất kỳ ứng dụng dựa trên thành phần nào), có các kiểu giao tiếp khác nhau tùy thuộc vào mối quan tâm mà chúng ta đang giải quyết và chúng có các kênh giao tiếp riêng.

Logic kinh doanh: đề cập đến mọi thứ cụ thể cho ứng dụng của bạn và mục tiêu của nó.

Logic trình bày: bất kỳ thứ gì mà người dùng tương tác hoặc kết quả từ sự tương tác từ người dùng.

Hai mối quan tâm này có liên quan đến các loại giao tiếp này:

  • Trạng thái ứng dụng
  • Cha-con
  • Cha mẹ trẻ em
  • Anh chị em ruột

Mỗi loại nên sử dụng đúng kênh liên lạc.


Kênh thông tin liên lạc

Kênh là một thuật ngữ lỏng lẻo mà tôi sẽ sử dụng để chỉ các triển khai cụ thể để trao đổi dữ liệu xung quanh ứng dụng Vue.

Đạo cụ: Logic trình bày giữa cha mẹ và con cái

Kênh giao tiếp đơn giản nhất trong Vue để giao tiếp trực tiếp giữa Cha mẹ và Con cái . Nó chủ yếu nên được sử dụng để chuyển dữ liệu liên quan đến logic trình bày hoặc một tập hợp dữ liệu bị hạn chế xuống hệ thống phân cấp.

Refs và phương pháp: Trình bày chống mẫu

Khi sử dụng một phương thức hỗ trợ để cho phép một đứa trẻ xử lý một sự kiện từ cha mẹ là không hợp lý, việc thiết lập một refthành phần con và gọi các phương thức của nó là tốt.

Đừng làm như vậy, đó là một kiểu chống đối. Suy nghĩ lại về kiến ​​trúc thành phần và luồng dữ liệu của bạn. Nếu bạn thấy mình muốn gọi một phương thức trên thành phần con từ cha mẹ, có lẽ đã đến lúc nâng trạng thái lên hoặc xem xét các cách khác được mô tả ở đây hoặc trong các câu trả lời khác.

Sự kiện: Logic trình bày giữa con-cha mẹ

$emit$on. Kênh giao tiếp đơn giản nhất để giao tiếp trực tiếp giữa Con và Cha. Một lần nữa, nên được sử dụng cho logic trình bày.

Xe buýt sự kiện

Hầu hết các câu trả lời đều đưa ra các lựa chọn thay thế tốt cho bus sự kiện, đây là một trong những kênh liên lạc có sẵn cho các thành phần ở xa hoặc bất cứ thứ gì trên thực tế.

Điều này có thể trở nên hữu ích khi chuyển các đạo cụ khắp nơi từ xa lên đến các thành phần con lồng nhau sâu sắc, hầu như không có thành phần nào khác cần những thành phần này ở giữa. Sử dụng một cách tiết kiệm cho dữ liệu được lựa chọn cẩn thận.

Hãy cẩn thận: Việc tạo tiếp theo của các thành phần tự ràng buộc với bus sự kiện sẽ bị ràng buộc nhiều lần - dẫn đến nhiều trình xử lý được kích hoạt và rò rỉ. Cá nhân tôi chưa bao giờ cảm thấy cần một bus sự kiện trong tất cả các ứng dụng trang đơn mà tôi đã thiết kế trước đây.

Phần sau minh họa cách một sai lầm đơn giản dẫn đến rò rỉ trong đó Itemthành phần vẫn kích hoạt ngay cả khi bị xóa khỏi DOM.

Hãy nhớ xóa người nghe trong destroyedhook vòng đời.

Cửa hàng tập trung (Logic kinh doanh)

Vuex là con đường đi cùng với Vue để quản lý nhà nước . Nó cung cấp nhiều hơn chỉ là các sự kiện và nó đã sẵn sàng cho ứng dụng quy mô đầy đủ.

Và bây giờ bạn hỏi :

[S] tôi có nên tạo cửa hàng của vuex cho mỗi lần giao tiếp nhỏ không?

Nó thực sự tỏa sáng khi:

  • xử lý logic kinh doanh của bạn,
  • giao tiếp với phần phụ trợ (hoặc bất kỳ lớp lưu trữ dữ liệu nào, như lưu trữ cục bộ)

Vì vậy, các thành phần của bạn có thể thực sự tập trung vào những thứ mà chúng có nghĩa là quản lý giao diện người dùng.

Nó không có nghĩa là bạn không thể sử dụng nó cho logic thành phần, nhưng tôi sẽ phạm vi logic đó đến một mô-đun Vuex có không gian tên chỉ với trạng thái giao diện người dùng toàn cầu cần thiết.

Để tránh đối phó với một mớ hỗn độn lớn của mọi thứ ở trạng thái toàn cầu, cửa hàng nên được tách biệt trong nhiều mô-đun không gian tên.


Các loại thành phần

Để sắp xếp tất cả các thông tin liên lạc này và để dễ dàng sử dụng lại, chúng ta nên nghĩ về các thành phần như hai loại khác nhau.

  • Vùng chứa ứng dụng cụ thể
  • Thành phần chung

Một lần nữa, điều đó không có nghĩa là một thành phần chung phải được sử dụng lại hoặc không thể sử dụng lại một vùng chứa ứng dụng cụ thể, nhưng chúng có các trách nhiệm khác nhau.

Vùng chứa ứng dụng cụ thể

Đây chỉ là thành phần Vue đơn giản bao bọc các thành phần Vue khác (vùng chứa chung hoặc các vùng chứa ứng dụng cụ thể khác). Đây là nơi giao tiếp với cửa hàng Vuex và vùng chứa này sẽ giao tiếp thông qua các phương tiện khác đơn giản hơn như đạo cụ và trình nghe sự kiện.

Những vùng chứa này thậm chí có thể không có phần tử DOM gốc nào cả và để cho các thành phần chung xử lý các tương tác của người dùng và tạo khuôn mẫu.

phạm vi bằng cách nào đó eventshoặc storeskhả năng hiển thị cho các thành phần anh chị em

Đây là nơi xảy ra phạm vi. Hầu hết các thành phần không biết về cửa hàng và thành phần này (chủ yếu) nên sử dụng một mô-đun cửa hàng không gian tên với một tập hợp giới hạn gettersactionsđược áp dụng với trình trợ giúp liên kết Vuex được cung cấp .

Thành phần chung

Chúng sẽ nhận dữ liệu của chúng từ các đạo cụ, thực hiện các thay đổi trên dữ liệu cục bộ của chính chúng và tạo ra các sự kiện đơn giản. Hầu hết thời gian, họ không nên biết một cửa hàng Vuex tồn tại.

Chúng cũng có thể được gọi là vùng chứa vì trách nhiệm duy nhất của chúng có thể là gửi đến các thành phần UI khác.


Liên lạc anh chị em

Vì vậy, sau tất cả những điều này, chúng ta nên giao tiếp giữa hai thành phần anh em như thế nào?

Dễ hiểu hơn với một ví dụ: giả sử chúng ta có một hộp nhập liệu và dữ liệu của nó phải được chia sẻ trên ứng dụng (anh chị em ở các vị trí khác nhau trong cây) và tồn tại với một chương trình phụ trợ.

Bắt đầu với tình huống xấu nhất , thành phần của chúng tôi sẽ kết hợp giữa trình bày và logic nghiệp vụ.

// 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>

Để tách biệt hai mối quan tâm này, chúng ta nên bọc thành phần của mình trong một vùng chứa ứng dụng cụ thể và giữ logic trình bày thành thành phần đầu vào chung của chúng ta.

Thành phần đầu vào của chúng tôi hiện có thể sử dụng lại và không biết về phụ trợ cũng như anh chị em.

// 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>

Vùng chứa ứng dụng cụ thể của chúng tôi hiện có thể là cầu nối giữa logic nghiệp vụ và giao tiếp trình bày.

// 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>

Vì các hành động của cửa hàng Vuex liên quan đến giao tiếp phụ trợ, vùng chứa của chúng tôi ở đây không cần biết về axios và phụ trợ.


3
Đồng ý với nhận xét về các phương pháp là " sự kết hợp giống nhau của việc sử dụng đạo cụ "
ghybs

Tôi thích câu trả lời này. Nhưng bạn có thể vui lòng giải thích thêm về Event Bus và ghi chú "Hãy cẩn thận:" không? Có lẽ bạn có thể đưa ra một số ví dụ, tôi không hiểu làm thế nào các thành phần có thể trở thành liên kết hai lần.
vandroid 21/09/18

Làm thế nào để bạn giao tiếp giữa thành phần mẹ và thành phần con lớn, ví dụ xác thực biểu mẫu. Trường hợp thành phần cha là một trang, con là biểu mẫu và con lớn là phần tử biểu mẫu đầu vào?
Lord Zed,

1
@vandroid Tôi đã tạo một ví dụ đơn giản hiển thị rò rỉ khi trình nghe không được xóa đúng cách, giống như mọi ví dụ trong chuỗi này.
Emile Bergeron

@LordZed Nó thực sự phụ thuộc, nhưng theo hiểu biết của tôi về tình huống của bạn, nó có vẻ như là một vấn đề thiết kế. Vue nên được sử dụng chủ yếu cho logic trình bày. Việc xác thực biểu mẫu nên được thực hiện ở nơi khác, chẳng hạn như trong giao diện API vani JS, mà một hành động Vuex sẽ gọi với dữ liệu từ biểu mẫu.
Emile Bergeron

10

Được rồi, chúng ta có thể giao tiếp giữa anh chị em qua cha mẹ bằng v-oncác sự kiện.

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

Giả sử rằng chúng ta muốn cập nhật Detailsthành phần khi chúng ta nhấp vào một số phần tử trong List.


trong Parent:

Bản mẫu:

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

Đây:

  • v-on:select-itemđó là một sự kiện, sẽ được gọi trong Listthành phần (xem bên dưới);
  • setSelectedItemđó là một Parentphương pháp để cập nhật selectedModel;

JS:

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

Trong List:

Bản mẫu:

<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"
  },
}
//...

Đây:

  • this.$emit('select-item', item)sẽ gửi hàng qua select-itemtrực tiếp trong cha mẹ. Và cha mẹ sẽ gửi nó để Detailsxem

5

Những gì tôi thường làm nếu tôi muốn "hack" các mẫu giao tiếp thông thường trong Vue, đặc biệt hiện nay không .syncđược dùng nữa, là tạo một EventEmitter đơn giản để xử lý giao tiếp giữa các thành phần. Từ một trong những dự án mới nhất của tôi:

import {EventEmitter} from 'events'

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

Với Transmitterđối tượng này, bạn có thể thực hiện, trong bất kỳ thành phần nào:

import Transmitter from './Transmitter'

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

Và để tạo thành phần "nhận":

import Transmitter from './Transmitter'

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

Một lần nữa, điều này dành cho các mục đích sử dụng thực sự cụ thể. Đừng đặt toàn bộ ứng dụng của bạn dựa trên mẫu này, Vuexthay vào đó hãy sử dụng một cái gì đó giống như vậy .


1
Tôi đã sử dụng vuex, nhưng một lần nữa, tôi có nên tạo cửa hàng của vuex cho mỗi lần giao tiếp nhỏ không?
Sergei Panfilov

Thật khó cho tôi để nói với lượng thông tin này, nhưng tôi muốn nói rằng nếu bạn đã sử dụng vuexcó, hãy tiếp tục. Sử dụng nó.
Hector Lorenzo

1
Trên thực tế, tôi sẽ không đồng ý rằng chúng ta cần sử dụng vuex cho mỗi giao tiếp nhỏ ...
Victor

Không, tất nhiên là không, tất cả phụ thuộc vào ngữ cảnh. Trên thực tế, câu trả lời của tôi di chuyển khỏi vuex. Mặt khác, tôi nhận thấy rằng bạn càng sử dụng vuex và khái niệm về đối tượng trạng thái trung tâm, thì tôi càng ít dựa vào giao tiếp giữa các đối tượng. Nhưng có, đồng ý, tất cả phụ thuộc.
Hector Lorenzo

3

Cách xử lý giao tiếp giữa anh chị em phụ thuộc vào tình huống. Nhưng trước tiên tôi muốn nhấn mạnh rằng phương pháp tiếp cận xe buýt sự kiện toàn cầu sẽ không còn ở Vue 3 . Xem RFC này . Do đó tại sao tôi quyết định viết một câu trả lời mới.

Mẫu tổ tiên phổ biến thấp nhất (hoặc “LCA”)

Đối với những trường hợp đơn giản, tôi thực sự khuyên bạn nên sử dụng mẫu Tổ tiên chung Thấp nhất (còn được gọi là "dữ liệu giảm, sự kiện tăng"). Mẫu này dễ đọc, triển khai, kiểm tra và gỡ lỗi.

Về bản chất, điều này có nghĩa là nếu hai thành phần cần giao tiếp, hãy đặt trạng thái được chia sẻ của chúng trong thành phần gần nhất mà cả hai đều chia sẻ như một tổ tiên. Truyền dữ liệu từ thành phần mẹ sang thành phần con thông qua đạo cụ và chuyển thông tin từ thành phần con sang thành phần mẹ bằng cách đưa ra một sự kiện (xem ví dụ về điều này ở cuối câu trả lời này).

Đối với một ví dụ có sẵn, trong một ứng dụng email, nếu thành phần "Tới" cần để tương tác với thành phần "nội dung thư", thì trạng thái của tương tác đó có thể nằm trong thành phần gốc của chúng (có thể là một thành phần được gọi email-form). Bạn có thể có một chỗ dựa trong phần email-formđược gọi addresseeđể nội dung thư có thể tự động thêm trước vào Dear {{addressee.name}}email dựa trên địa chỉ email của người nhận.

LCA trở nên khó khăn nếu giao tiếp phải đi một quãng đường dài với nhiều thành phần trung gian. Tôi thường giới thiệu đồng nghiệp về bài đăng trên blog xuất sắc này . (Bỏ qua thực tế là các ví dụ của nó sử dụng Ember; các ý tưởng của nó có thể áp dụng trên nhiều khung giao diện người dùng.)

Mẫu vùng chứa dữ liệu (ví dụ: Vuex)

Đối với các trường hợp phức tạp hoặc tình huống mà giao tiếp giữa cha mẹ và con cái sẽ liên quan đến quá nhiều người trung gian, hãy sử dụng Vuex hoặc một công nghệ chứa dữ liệu tương đương. Khi thích hợp, hãy sử dụng mô-đun không gian tên .

Ví dụ, có thể hợp lý khi tạo một không gian tên riêng cho một tập hợp phức tạp của các thành phần có nhiều kết nối với nhau, chẳng hạn như thành phần lịch đầy đủ tính năng.

Mẫu Xuất bản / Đăng ký (Xe buýt Sự kiện)

Nếu mẫu bus sự kiện (hoặc "xuất bản / đăng ký") phù hợp hơn với nhu cầu của bạn, nhóm cốt lõi của Vue hiện khuyên bạn nên sử dụng thư viện của bên thứ ba như mitt . (Xem RFC được tham chiếu trong đoạn 1.)

Phần thưởng lan man và mã

Đây là một ví dụ cơ bản về giải pháp Tổ tiên Chung Thấp nhất để giao tiếp giữa anh chị em với anh chị em, được minh họa qua trò chơi whack-a-nốt ruồi .

Một cách tiếp cận ngây thơ có thể là nghĩ, "nốt ruồi 1 nên nói với nốt ruồi 2 xuất hiện sau khi nó bị đánh thủng". Nhưng Vue không khuyến khích kiểu tiếp cận này, vì nó muốn chúng ta suy nghĩ về cấu trúc cây .

Đây có thể là một điều rất tốt. Một ứng dụng không tầm thường nơi các nút giao tiếp trực tiếp với nhau trên các cây DOM sẽ rất khó gỡ lỗi nếu không có một số loại hệ thống kế toán (như Vuex cung cấp). Trên hết, các thành phần sử dụng "dữ liệu giảm, sự kiện tăng lên" có xu hướng thể hiện khả năng ghép nối thấp và khả năng tái sử dụng cao — cả hai đặc điểm rất mong muốn giúp ứng dụng lớn mở rộng.

Trong ví dụ này, khi một nốt ruồi bị đánh thủng, nó sẽ phát ra một sự kiện. Thành phần quản lý trò chơi quyết định trạng thái mới của ứng dụng là gì và do đó, chú chuột con biết rõ phải làm gì sau khi Vue hiển thị lại. Đó là một ví dụ "tổ tiên chung thấp nhất" hơi tầm thường.

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>

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.