在使用 vue-cli 创建的 vue 项目中进行组件间的通信,以下是三种常用的组件间通信的方式总结。
1、父组件向子组件传递数据----------------------------> 通过Prop
2、子组件向父组件发送消息---------------------------->使用 $emit
3、组件间与组件间通信----------------------------------->使用Vuex
父组件向子组件传递数据
$ vue init webpack vue-demo
首先通过Vue-cli命令创建一个webpack模板项目,为项目简单起见,这里在Install vue-router、insatll ESLint、install unit test都选择NO。
删除掉src/components目录下面的HelloWorld.vue组件,在该目录下新建一个Child.vue组件,现在的目录结构长这样。
准备工作完毕,接下来看一下 父组件利用props往子组件传输数据。
修改Child.vue组件:
用一个 props
选项将要传递的属性包含在该组件可接受的 prop 列表中,当一个值传递给一个 prop 特性的时候,它就变成了那个组件实例的一个属性,这里则传递了一个msg属性。
<template>
<div class="mychild">
<h2>子组件</h2>
<p>{{msg}}</p>
</div>
</template>
<script>
export default{
props:["msg"],
}
</script>
<style>
.mychild
{
border:1px solid black;
width:400px;
}
</style>
修改App.vue组件:
这里使用了 v-bind
来动态传递 prop,也就是说当parentMsg改变的时候,子组件对应的值也会改变。这里添加了一个按钮来改变parentMsg值,用于观察。
<template>
<div id="app">
<button v-on:click="changeMsg">改变msg内容</button>
<child v-bind:msg="parentMsg"></child>
</div>
</template>
<script>
import Child from './components/Child'
export default {
name: 'App',
data(){
return {
parentMsg:'original parent message'
}
},
components: {
Child
},
methods:{
changeMsg:function(){
console.log('1');
this.parentMsg='parent message has changed!'
}
}
}
</script>
<style>
</style>
启动项目 :
$ npm run dev
运行可以看到父组件向子组件传递了一个数据,点击改点msg内容按钮,对应子组件的值也改变了。
注意:在利用props实现传值的过程中理论上是要实现单向传递,即父组件改变相关参数的值,子组件也相应变化,但是子组件对参数的改变不应该影响父组件。但是当props中接收的是父组件传递的引用类型(对象或者是数组)时,在子组件中对数据改变时,父组件中的数据也会相应的改变,因为两者是指向的同一地址内存。如果不想子组件的改变影响父组件可以利用深拷贝,将接受的数据进行深拷贝后在子组件中使用,而不直接操作接受的数据。深拷贝可以直接利用ES6中的obj=Object。assign({},myMessage)(在computed中定义),这样子组件的改动将不会影响到父组件。
子组件向父组件发送消息
Vue 实例提供了一个自定义事件的系统来解决这个问题。我们可以调用内建的 $emit
方法并传入事件的名字,来向父级组件触发一个事件,然后我们可以用 v-on
在父组件上监听这个事件,就像监听一个原生 DOM 事件一样。
同样用上面的这个项目,修改child.vue组件:
在点击button的时候,会使用$emit向父组件派发一个事件
<template>
<div class="mychild">
<h2>子组件</h2>
<button v-on:click="sendMsgToParent">向父组件传值</button>
</div>
</template>
<script>
export default{
methods:{
sendMsgToParent:function(){
this.$emit('myevent',"some message from child!");
}
}
}
</script>
<style>
.mychild
{
border:1px solid black;
width:400px;
}
</style>
修改App.vue:
在父级组件监听myevent这个事件,在事件处理函数中,值将会作为第一个参数传入这个方法。以此获取到子组件向父组件发送的消息。
<template>
<div id="app">
<div>
msg:{{msgFromChild}}
</div>
<child v-on:myevent="getMsgFromChild"></child>
</div>
</template>
<script>
import Child from './components/Child'
export default {
name: 'App',
data(){
return{
msgFromChild:''
}
},
components: {
Child
},
methods:{
getMsgFromChild:function(data){
this.msgFromChild=data;
}
}
}
</script>
<style>
</style>
启动项目 :
$ npm run dev
运行后点击按钮可以看到利用事件机制子组件向父组件传递了一个参数
注意: 当有组件嵌套时则需要利用该机制一层一层的触发到指定层,不然直接在顶层监听子组件的子组件的事件是监听不到的,需要先向父组件派发,父组件在向上层触发
使用Vuex让多个vue组件中共享状态实现组件通讯
Vuex是一个专为Vue开发的应用程序的状态管理模式,它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
Vuex特点:
- 状态存储是响应式:当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
- 不能随意修改Vuex的状态:改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。
同样是上面这个项目,我们首先要安装Vuex
$ npm install vuex --save
接下来在src目录下面创建一个store.js文件,在src/components下面创建OtherChild.vue文件。现在的目录结构长这样:
修改main.js,引入store
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import store from './store'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
store,
el: '#app',
components: { App },
template: '<App/>'
})
store.js:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
someMsg:'some msg'
},
mutations: {
change_msg(state, data)
{
state.someMsg=data;
}
}
})
App.vue:
<template>
<div id="app">
<Child></Child>
<OtherChild></OtherChild>
</div>
</template>
<script>
import Child from './components/Child';
import OtherChild from './components/OtherChild.vue';
export default {
name: 'App',
components: {
Child,
OtherChild
}
}
</script>
<style>
</style>
Child.vue:
<template>
<div class="mychild">
<h3>子组件1</h3>
<div>
{{msg}}
</div>
<button v-on:click="changeState">子组件1改变状态</button>
</div>
</template>
<script>
export default{
computed: {
// 返回store中的值
msg() {
return this.$store.state.someMsg;
}
},
methods:{
changeState:function(){
console.log('1');
this.$store.commit('change_msg','子组件1改变了msg');
}
}
}
</script>
<style>
.mychild
{
border:1px solid black;
width:400px;
}
</style>
OtherChild.vue:
<template>
<div class="mychild">
<h3>子组件2</h3>
<div>
{{msg}}
</div>
<button v-on:click="changeState">子组件2 改变状态</button>
</div>
</template>
<script>
export default{
computed: {
// 返回store中的值
msg() {
return this.$store.state.someMsg;
}
},
methods:{
changeState:function(){
this.$store.commit('change_msg','子组件2 改变了msg');
}
}
}
</script>
<style>
.mychild
{
border:1px solid black;
width:400px;
}
</style>
启动项目 :
$ npm run dev
运行后分别点击组件1下面的button和组件2下面的button。可以看到使用vuex让组件间的状态进行了集中的管理,而且Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。