这一节咱们一块儿看看 vue
中组件间的数据是如何传递的。javascript
前面,咱们已经初步创建了 vue
组件化的思想,知道如何建立组件、引入组件以及如何在组件里的一些功能。接下来,咱们来学习怎么创建组件之间的链接,也就是组件的通信。直白一点说就是:在一个组件中作的操做如何更新到应用程序中的其余组件。css
这篇文章会从两个方便介绍 vue
组件间的通信:html
- 父子组件之间的通信 - 兄弟组件之间的通信
<!-- more -->vue
在 vue
中,将数据从父组件传递到子组件,能够用 props
来实现(这一点,在 React
中也是如此)。java
props
指的是从外部(父组件)设置的属性。同时,为了告诉 vue
子组件须要从自已的外部(父组件)接收数据,须要在子组件的 vue
对象中设置 props
属性。这个属性是一个 String
数组,每一个字符串表示一个能够从父组件接收的属性。react
咱们须要作两件事情:父组件使用属性绑定、子组件使用 props 对象接收。 看个例子:git
<template> <div> <ChildCom :list='list' :run='run' :home='this'></ChildCom> </div> </template> <script> import ChildCom from './ChildCom'; export default { data() { return { list: ['我是父组件里面的数据', '我来自父组件'], }; }, components: { ChildCom, }, methods: { run() { alert('我是父组件里面的方法'); // eslint-disable-line }, }, }; </script>
咱们在父组件 ParentCom
里面引入了子组件 ChildCom
。为了将数据从父组件传到子组件,咱们在子组件 ChildCom
上绑定了几个属性:github
<childCom :list='list' :run='run' :home='this'></childCom>
绑定属性的时候,属性名前须要加冒号。这里咱们绑定了三个属性,父组件的 data
中的 list
、methods
中的 run
方法以及指向父组件的 this
。vuex
接下来,咱们建立一个 ChildCom
组件,经过子组件的 props
选项来得到父组件传过来的数据:数组
<template> <div> <div class='list'> <ul> <li v-for='item in list' :key='item'>{{ item }}</li> </ul> </div> <div class='buttons'> <button @click='run'>执行父组件的方法</button> <button @click='getParent()'>获取父组件的数据和方法</button> </div> </div> </template> <script> export default { methods: { getParent() { alert(this.home); // eslint-disable-line alert(this.home.list); // eslint-disable-line alert(this.home.run); // eslint-disable-line }, }, props: ['list', 'run', 'home'], }; </script> <style lang="postcss" scoped> .list { margin-bottom: 10px; } li { margin: 10px 0; list-style: none; } button { padding: 6px; background-color: #35b880; border: none; color: white; font-size: 16px; margin: 5px; } </style>
子组件的 props
中接收了父组件传递下来的属性。须要注意的是,props
字符串数组中的值(prop
)要和在父组件中为子组件绑定属性的属性名保持一致。
这里咱们加了一些样式,在 App.vue
中引入父组件 ParentCom
,打开浏览器会看到:
这样,在子组件中就拿到了父组件传递下来的数据和方法以及父组件自己,点击按钮就能够查看到父组件传递给子组件的数据。
前面咱们知道了父组件如何向子组件通信,那子组件如何向父组件通信呢?这里介绍两种方式:
props
的形式传给子组件(借鉴 react 的父子通信方式)首先在子组件 ChildCom
的 <template>
中添加一个新的标签 button
。在这个 button
上添加一个click
事件:
<div class='buttons'> <!-- add this --> <button @click='submit("我是子组件传递给父组件的数据")'>子组件触发更改父组件的数据</button> </div>
当咱们点击这个按钮的时候,想要执行 submit
方法,咱们在子组件的 <script>
中添加这个方法:
methods: { getParent() { alert(this.home); // eslint-disable-line alert(this.home.list); // eslint-disable-line alert(this.home.run); // eslint-disable-line alert(this.home.appendToList); // eslint-disable-line }, // add this submit(text) { this.$emit('addItem', text); }, },
触发事件时发出($emit
)自定义的事件: addItem
,这里咱们也给 addItem
事件传递了一个 text
参数。这样就完成了子组件发出自定义事件的过程。
接下来须要在父组件中监听子组件传递的自定义事件 addItem
。怎么作呢?
在父组件中给子组件绑定监听子组件中自定义的事件的方法。这就意味着咱们也须要在父组件中定义这个方法。看看代码:
<template> <div> <ChildCom :list='list' :run='run' :home='this' @addItem='addItem'></ChildCom> </div> </template> <script> import ChildCom from './ChildCom'; export default { data() { return { list: ['我是父组件里面的数据', '我来自父组件'], }; }, components: { ChildCom, }, methods: { run() { alert('我是父组件里面的方法'); // eslint-disable-line }, addItem(item) { this.list.push(item); }, }, }; </script>
在子组件上绑定监听子组件中自定义事件的方法须要使用 @
符号,在 methods
中添加了 addItem
方法。这时候,咱们打开浏览器,点击第三个按钮,就会看到子组件向父组件传递的数据了。
传递 props
的意思是说在父组件里面定义改变父组件数据的方法,而后经过 props
传递给子组件,这样子组件就能够触发执行从父组件传递下来的方法,达到更改父组件数据的目的。这种方法借鉴了 React
中组件通信的方式。看看代码:
咱们依旧使用上面的代码,在 ParentCom
组件中将 addItem
方法传递给子组件:
<ChildCom :list='list' :run='run' :home='this' @addItem='addItem' :addItem='addItem'></ChildCom>
在子组件 ChildCom
中添加一个 button
,在它的点击事件中执行父组件的 addItem
方法,因此,咱们也须要在子组件的 props
选项中把 addItem
方法添加进去:
<template> <div> <div class='list'> <ul> <li v-for='item in list' :key='item'>{{ item }}</li> </ul> </div> <div class='buttons'> <button @click='run'>执行父组件的方法</button> <button @click='getParent()'>获取父组件的数据和方法</button> <button @click='submit("我是子组件传递给父组件的数据")'>子组件触发更改父组件的数据</button> <!-- add this --> <button @click='addItem("我是经过子组件props方式传递给父组件的数据")'>子组件触发更改父组件的数据-2</button> </div> </div> </template> <script> export default { data() { return {}; }, methods: { getParent() { alert(this.home); // eslint-disable-line alert(this.home.list); // eslint-disable-line alert(this.home.run); // eslint-disable-line alert(this.home.appendToList); // eslint-disable-line }, submit(text) { this.$emit('addItem', text); }, }, // add this props: ['list', 'run', 'home', 'addItem'], }; </script>
打开浏览器,点击 button
:
在 vue
中实现兄弟组件间的通信主要有两种方法:经过父组件进行兄弟组件之间通信、经过 EventHub
进行兄弟组件间通信。为了避免和上面讲述的父子组件之间通信的代码混淆,这里咱们从新新建组件来演示:
ParentCard
BrotherCard
和 SisterCard
简单来讲,就是让兄弟组件经过一个共同父组件进行通信。
首先建立父组件 ParentCard
:
<template> <div class='container'> <h2>父组件</h2> <button @click='stopCommunicate' v-if='showButton'>中止通信</button> <div class='card-body'> <brother-card :messageSon='messageson' @brotherSaid='messageDaughter' class='card-brother'></brother-card> <sister-card :messageDaughter='messagedaughter' @sisterSaid='messageSon' class='card-sister'></sister-card> </div> </div> </template> <script> import BrotherCard from './BrotherCard'; import SisterCard from './SisterCard'; export default { name: 'ParentCard', data() { return { messagedaughter: '', messageson: '', }; }, components: { BrotherCard, SisterCard }, methods: { messageDaughter(message) { this.messagedaughter = message; }, messageSon(message) { this.messageson = message; }, showButton() { return this.messagedaughter && this.messageson; }, stopCommunicate() { this.messagedaughter = ''; this.messageson = ''; }, }, }; </script> <style scoped> .container { width: 70%; margin: 10px auto; border-radius: 10px; box-shadow: 1px 1px 1px 1px rgba(50, 50, 93, 0.1), 0 5px 15px rgba(0, 0, 0, 0.07) !important; padding: 30px; } .card-body { display: flex; justify-content: center; } .card-brother, .card-sister { margin: 0 50px; } </style>
建立 BrotherCard
组件:
<template> <div> <div> <p>我是子组件:Brother</p> <button @click='messageSister'>给妹妹发消息</button> <div v-if='messageSon' v-html='messageSon'></div> </div> </div> </template> <script> export default { name: 'BrotherCard', props: ['messageSon'], methods: { messageSister() { this.$emit('brotherSaid', 'Hi,妹妹'); }, }, }; </script>
建立 SisterCard
组件:
<template> <div> <div> <p>我是子组件:Sister</p> <button @click='messageBrother'>给哥哥发消息</button> <div v-if='messageDaughter' v-html='messageDaughter'></div> </div> </div> </template> <script> export default { name: 'SisterCard', props: ['messageDaughter'], methods: { messageBrother() { this.$emit('sisterSaid', 'Hi,哥哥'); }, }, }; </script>
结果以下:
在学习完父子组件之间的通信方法以后,经过父组件进行兄弟组件的通信就很简单了,其实就是把兄弟之间须要共享的数据提高至他们最近的父组件当中进行管理,将他们的父组件做为中间媒介(在 React
中把这种方式被称为状态提高)。
在 vue1.0
中,组件之间的通讯主要经过 $dispatch
沿着父链向上传播和经过 $broadcast
向下广播来实现。可是在 vue2.0
中 $dispatch
和 $broadcast
已经被弃用。
vue
中也提供了相似 Redux
的组件通讯和状态管理方案:vuex
。对于中大型的项目来讲,使用 vuex
是一个很好的选择。可是对于小型的项目来讲,若是一开始就引入了 vuex
,是彻底不必的。
vue
官方文档中也给出了$dispatch
和 $broadcast
最简单的升级方式就是:经过使用事件中心,容许组件自由交流,不管组件处于组件树的哪一层。 vue
文档中把这个事件中心命名为 eventHub
,也有不少其余教程中将其命名为 eventBus
。在本教程中,咱们统一命名为 eventHub
。
咱们一样基于上面的示例来作修改:ParentCard
组件包含了 SisterCard
和 BrotherCard
两个子组件,并且这两个子组件是兄弟组件。
首先在 main.js
文件中定义一个新的 eventHub
对象(vue
实例 ):
import Vue from 'vue'; import App from './App'; import router from './router'; Vue.config.productionTip = false; // add this export const eventHub = new Vue(); // eslint-disable-line /* eslint-disable no-new */ new Vue({ el: '#app', router, components: { App }, template: '<App/>', });
接着咱们要作的是让 eventHub
实例成为 BrotherCard
组件中发出事件的实例,使用 eventHub.$emit
来替代上例中的 this.$emit
(由于 eventHub
是一个 vue
实例,因此它可使用 $emit
方法)。
<template> <div> <p>我是Brother组件</p> <button @click='messageSister'>给妹妹发消息</button> <div v-if='fromSister' v-html='fromSister'></div> </div> </template> <script> import { eventHub } from '../../main'; export default { name: 'BrotherCard', data: () => ({ fromSister: '', }), methods: { messageSister() { eventHub.$emit('brotherSaid', 'Hi,妹妹'); }, }, /* eslint-disable */ created() { eventHub.$on('sisterSaid', message => { this.fromSister = message; }); }, }; </script>
引入 main.js
,而且将 created()
生命周期钩子添加到 BrotherCard
组件中。在 created()
钩子函数中添加 eventHub
启动自定义事件的监听器,监听 sisterSaid
这个动做。
接下来咱们改造下 SisterCard
组件,和 BrotherCard
组件的改造是同样的:
<template> <div> <p>我是Sister组件</p> <button @click='messageBrother' class='btn'>给哥哥发消息</button> <div v-if='fromBrother' v-html='fromBrother'></div> </div> </template> <script> import { eventHub } from '../../main'; export default { name: 'SisterCard', data: () => ({ fromBrother: '', }), methods: { messageBrother() { eventHub.$emit('sisterSaid', 'Hi,哥哥'); }, }, /* eslint-disable */ created() { eventHub.$on('brotherSaid', message => { this.fromBrother = message; }); }, }; </script>
这时候,咱们就不用在父组件 ParentCard
作任何操做了:
<template> <div class='container'> <h2>父组件</h2> <div class='card-body'> <brother-card class='card-brother'></brother-card> <sister-card class='card-sister'></sister-card> </div> </div> </template> <script> import BrotherCard from './BrotherCard'; import SisterCard from './SisterCard'; export default { name: 'ParentCard', components: { 'brother-card': BrotherCard, 'sister-card': SisterCard, }, }; </script>
打开浏览器,能够看到这样也实现了兄弟组件之间的通信。
这里的全局模式指的是建立全局变量和全局方法,让其余组件之间共享数据存储的模式。咱们看看怎么操做:
先建立一个 store.js
,在这个 JS
文件里建立全局的变量和方法:
const store = { state: { numbers: [1, 2, 3] }, addNumber(newNumber) { this.state.numbers.push(newNumber); }, }; export default store;
在 store
的 state
中存放了一个 numbers
数组和一个 addNumber
方法。接下来咱们建立两个组件:
NumberDisplay
组件:用来显示来自 store
的 numbers
数组NumberSubmit
组件:容许用户向数据数组中添加一个新的数字建立 NumberDisplay
组件:
<template> <div> <h2>{{ storeState.numbers }}</h2> </div> </template> <script> import store from './store'; export default { name: 'NumberDisplay', data() { return { storeState: store.state, }; }, }; </script>
建立 NumberSubmit
组件:
<template> <div class='form'> <input v-model='numberInput' type='number'> <button @click='addNumber(numberInput)'>Add new number</button> </div> </template> <script> import store from './store'; export default { name: 'NumberSubmit', data() { return { numberInput: 0, }; }, methods: { addNumber(numberInput) { store.addNumber(Number(numberInput)); }, }, }; </script>
接着在 GlobalMode.vue
中引用刚才建立的组件:
<template> <div> <NumberDisplay/> <NumberSubmit/> </div> </template> <script> import NumberDisplay from '../components/pass-data-3/NumberDisplay'; import NumberSubmit from '../components/pass-data-3/NumberSubmit'; export default { name: 'GlobalMode', components: { NumberDisplay, NumberSubmit }, }; </script>
效果以下:
能够看到,咱们使用这种方式也能够实现组件间的通信。
最后,咱们画个图总结一下 Vue
组件间的通信:
本节内容代码地址: https://github.com/IDeepspace...
欢迎关注个人博客: https://togoblog.cn/