vue用组件化简化了我们编写代码的复杂度,组件之间经常会出现数据传递的情况,那么组件之间是怎样通信的呢?
使用props传递数据
组件实例的作用域是孤立的。这意味着不能 (也不应该) 在子组件的模板内直接引用父组件的数据。父组件的数据需要通过 prop 才能下发到子组件中。
假设我们现有一个这样的父组件
var vm = new Vue({
el: "#app",
data: {
parentMessage: 'h'
},
components: {
Child
}
})
可以看到在该父组件中注册了一个子组件Child,还有一条data数据parentMessage
接下来我们怎样在子组件Child中拿到parentMessage呢?
在子组件中这样写
var Child = {
//声明当前组件内部能够接受一个message的属性,如果是驼峰式命名,在传参数的时候使用短横线连接的小写
props: ["message", "myMessage"],
template: '<span>{{message}} {{myMessage}}</span>'
}
在子组件中我们在props中写了一个自定义属性mymessage来接收从父组件中传来的数据,然后通过该自定义属性在父组件中传递
<div id="app">
<child message="hello" :my-message="parentMessage"></child>
<child message="hi"></child>
<input type="text" v-model="parentMessage">
</div>
上述代码中我们使用自定义的属性my-message接收到了父组件中定义的数据parentMessage,然后再将这个数据传递到子组件中去显示,效果如下,我们拿到了父组件中的parentMessage
props接收后对数据的修改
在这里我只记录一下用子组件的计算属性去修改从父组件中拿到的数据
<div id="app">
<child :size="parentSize"></child>
</div>
<script src="./vue.js"></script>
<script>
var Child = {
template: '<div>{{size}}</div>',
props: ['size'],
}
var vm = new Vue({
el: '#app',
data: {
parentSize: 'THREE'
},
components: {
Child
}
})
</script>
如上代码,子组件中利用前面讲到的方法拿到了父组件中传递的parentSize,假如子组件直接写一个方法去修改父组件的数据在vue的单向数据流中是不可能的,这里我们可以将parentSize放到一个计算属性中去操作,相当于另外创建了一个对象
所以我们在子组件Child中写一个computed计算属性,其中写一条属性normalSize,来将size全部转换为小写
computed: {
normalSize() {
return this.size.trim().toLowerCase();
}
}
在子组件的模板中添加上计算属性normalSize的显示
template: '<div>{{size}} {{normalSize}}</div>'
结果如下
遍历并显示父组件传来的数组
在父组件中的data声明了一个数组lis
var vm = new Vue({
el: "#app",
data: {
lis: ['a', 'b', 'c']
},
components: {
List
}
})
在子组件中得到并遍历
var List = {
template: '<div><div v-for="item in lists">{{item}}</div></div>',
props: ['lists']
}
然后在父组件模板中将子组件显示出来
<div id="app">
<list :lists="lis"></list>
</div>
这里要注意数据传递的过程
首先还是像之前一样通过自定义属性在父组件模板中得到数组,然后传递到子组件的模板中去使用
非父子组件之间的通信
要实现非父子组件之间的通信就不得不说到$emit
发出的可以被自定义监听的事件,这里我通过一个小示例来解释一下
子组件事件实现父组件数据的变化
这里是一个点击子组件让父组件中count统计点击次数的示例
<div id="app">
{{total}}
<!--在child组件中添加一个监听事件来监听自定义事件add-parent-total,当触发本事件时执行父组件中的addTotal方法使得total改变-->
<child @add-parent-total="addTotal"></child>
<child></child>
</div>
<script src="vue.js" charset="utf-8"></script>
<script>
var Child = {
template: '<button @click="add">{{counter}}</button>',//子组件模板的button中添加一个点击事件,来调用子组件中的方法,add
data() {
return {
counter: 0
}
},
methods: {
//在子组件中写一个方法来通过$emit触发一个自定义的add-parent-total事件
add() {
this.counter++;
this.$emit('add-parent-total');
}
}
}
var vm = new Vue({
el: '#app',
data: {
total: 0
},
methods: {
addTotal(preload) {//在父组件添加一个方法,来操作父组件中的数据total
this.total += 1;
}
},
components: {
Child
}
})
</script>
在上面的例子中我们还是无法绕过子组件无法改变父组件中内容的事实,但是我们通过触发父组件模板中的一个自定义事件,让父组件自己调用自己的方法,改变了自己的内容,利用了自定义事件和$emit的方式
非父子组件通信
在非父子组件通信的时候我们通过一个空的vue对象来实现
首先声明一个vue实例
var vm = new Vue({
el: '#app',
data: {
},
components: {
AppHead,
AppBody
}
})
在实例中注册了两个组件,他们两个算是兄弟组件,我们就让这两个兄弟组件实现通信
接着声明一个空的vue实例
var bus = new Vue()
然后去写两个子组件
var AppHead = {
template: '<div><button @click="add">添加</button></div>',
methods: {
add() {
bus.$emit('change', 1)
}
}
}
var AppBody = {
template: `<div>{{counter}}</div>`,
data() {
return {
counter: 0
}
},
created() {
bus.$on('change', count => {
this.counter += count
})
}
}
在AppHead组件中我们让它在被点击是触发一个add方法,这个add 方法用来发出一个基于bus发出的自定义事件change
然后在AppBody中写了一个在AppBody被创建之后就会时刻监听bus 的change事件的方法,这样一旦change事件被触发就会执行这个监听事件,实现了兄弟组件之间的通信
添加上父组件的模板将两个兄弟组件放进去实现效果
<div id="app">
<app-head></app-head>
<app-body></app-body>
</div>