vue.js组件通讯

为了演示说明vue的组件通讯,我们从一个假设的todo应用开始(再次:)。UI当然还是类似这样的: 但是这次的新意在于,我们会把整

为了演示说明vue的组件通讯,我们从一个假设的todo应用开始(再次:)。UI当然还是类似这样的:

vue.js组件通讯

但是这次的新意在于,我们会把整个app分为三个组件,层次关系如下:

app

--newTodo

--todoList

app作为父应用组件,newTodo作为一个子组件负责用户输入,并且获取新的todo,而todoList作为另外一个子组件则负责显示全部todo。

这样的分工在稍微大的app中非常常见,由此达成分而治之的目的。但是组件之间势必需要通讯,比如newTodo组件必须把新的todo字符串通知到todoList组件,以便后者更新todo列表并由此更新用户界面。

基于组件结构的通讯

从子组件到父组件的通讯,vue.js提供了$dispatch方法,而从父组件到子组件则是通过 $broadcast方法。我们在如下的代码中使用了此技术:

<html>

<head>

<script src="https://cdn.jsdelivr.net/vue/1.0.28/vue.min.js"></script>

</head>

<body>

<div id="todo-app">

<h1>todo app</h1>

<new-todo></new-todo>

<todo-list></todo-list>

<div>

<script>

var newTodo = {

template:'<div><input type="text" autofocus v-model="newtodo"/><button @click="add">add</button></div>',

data(){

return{

newtodo:''

}

},

methods:{

add:function(){

this.$dispatch('newtodo',this.newtodo)

this.newtodo = ''

}

}

}

var todoList = {

template:'<ul> /

<li v-for="(index,item) in items">{{item}} /

<button @click="rm(index)">X</button></li> /

</ul>',

data(){

return{

items:['item 1','item 2','item 3'],

}

},

methods:{

rm:function(i){

this.items.splice(i,1)

}

},

events: {

'newtodo': function (newtodo) {

this.items.push(newtodo)

}

},

}

var app= new Vue({

el:'#todo-app',

components:{

newTodo:newTodo,

todoList:todoList

},

events: {

'newtodo': function (newtodo) {

this.$broadcast('newtodo',newtodo)

}

}

})

</script>

</body>

</html>

组件newTodo在用户点击按钮后会把新的todo字符串通过$dispatch发出,而父组件app在event内截获此事件,随即通过$broadcast方法发送到子组件,子组件todoList在event内截获此事件取出payload,加入它到数据items内。这就是组件通讯的方法。vue.js并没有提供兄弟组件直接的直接通讯方法,如果兄弟组件之间需要通讯,只能首先发给父组件,父组件想子组件广播,侦听此事件的子组件随后获取此事件。

通过$broadcast+$dispatch完成组件通讯是可行的,但是问题不少:

  1. 依赖于树形组件结构,你得知道组件的结构是怎么样的

  2. 组件结构复杂的话,必然降低通讯效率

  3. 兄弟组件直接不能直接通讯,必须通过父组件间接完成

集中化的eventBus

实际上,我们只是为了让两个组件交换数据,这个过程并不应该和组件的结构(父子关系的组件,兄弟关系的组件)捆绑在一起。因此,一个变化的结构,是引入一个新的组件,此组件脱离组件关系,作为兄弟关系的组件之间的通讯中介。此技术被称为Event Bus。如下代码正式利用了此技术:

<html>

<head>

<script src="https://cdn.jsdelivr.net/vue/1.0.28/vue.min.js"></script>

</head>

<body>

<div id="todo-app">

<h1>todo app</h1>

<new-todo></new-todo>

<todo-list></todo-list>

<div>

<script>

var eventHub =new Vue( {

data(){

return{

todos:['A','B','C']

}

},

created: function () {

this.$on('add', this.addTodo)

this.$on('delete', this.deleteTodo)

},

beforeDestroy: function () {

this.$off('add', this.addTodo)

this.$off('delete', this.deleteTodo)

},

methods: {

addTodo: function (newTodo) {

this.todos.push(newTodo)

},

deleteTodo: function (i) {

this.todos.splice(i,1)

}

}

})

var newTodo = {

template:'<div><input type="text" autofocus v-model="newtodo"/><button @click="add">add</button></div>',

data(){

return{

newtodo:''

}

},

methods:{

add:function(){

eventHub.$emit('add', this.newtodo)

this.newtodo = ''

}

}

}

var todoList = {

template:'<ul> /

<li v-for="(index,item) in items">{{item}} /

<button @click="rm(index)">X</button></li> /

</ul>',

data(){

return{

items:eventHub.todos

}

},

methods:{

rm:function(i){

eventHub.$emit('delete', i)

}

}

}

var app= new Vue({

el:'#todo-app',

components:{

newTodo:newTodo,

todoList:todoList

}

})

</script>

</body>

</html>

由此代码我们可以看到:

  1. app组件不再承担通讯中介功能,而只是简单的作为两个子组件的容器

  2. EventBus组件承载了全部的数据(todos),以及对数据的修改,它监听事件add和delete,在监听函数内操作数据

  3. 子组件todoList的data成员的数据来源改为从eventBus获取,删除todo的方法内不再操作数据,而是转发给eventBus来完成删除

  4. 子组件newTodo的按钮不再添加数据,而是转发事件给eventBus,由后者完成添加

这样做就把本来捆绑到组件结构上的通讯还原为单纯的通讯,并且集中数据和操作到一个对象(eventBus)也就有利于组件的数据共享。

当我们谈到eventBus的时候,我们离vuex就比较进了。了解vuex可以看此文章:

https://segmentfault.com/a/11...

ref

Learn Vuex by Building a Notes App

Build a Simple Todo App with Vue.js and Vuex 2.0

简单能用,但是没有体现出父子组件通讯的替代性。 https://github.com/wilf312/vu...

Vue components communication http://stackoverflow.com/ques...

关于父子通讯,这里写的很好: https://vuejs.org/v2/guide/mi...

作者:刘传君

创建过产品,创过业。好读书,求甚解。

可以通过 1000copy#gmail.com 联系到我

出品

bootstrap小书 https://www.gitbook.com/book/...

http小书 http://www.ituring.com.cn/boo...

Git小书 http://www.ituring.com.cn/boo...

未登录用户
全部评论0
到底啦