在vue中组件之间的传值从大类上讲有父子组件和兄弟组件的传值,其他的小类还有通过状态管理器,或者是路由中的params和query。其中vuex状态管理本质上是通过共享数据实现的,路由方面则是通过附加额外参数(这个时候数据和route是高度耦合的,你只有通过$route对象才能拿到你需要的数据,所以为了实现数据和路由的解耦,我们还可以在设置路由的时候通过props去解决这个问题,详细操作可见路由官网的路由组件传参部分)。
父组件通过prop向子组件传值,子组件通过this.$emit触发父组件中通过v-on监听的事件来完成一次数据的变更。子组件内是无法直接修改prop的值的,不然会有警告。为什么不能直接修改呢,因为这破坏了父子组件之间数据的单向传递,导致数据的变化难以追踪。我们可以看到子组件通知父组件变化,父组件是需要v-on监听一个对应的函数才能了解到变化,虽然这种对应关系很明确,但如果我有多个prop,而且每个prop都需要不同的$emit去触发监听事件,那么对应的父组件就要有多个不同的函数仅仅是为了监听到多个prop的变化,这其实有点浪费。那么我们有没有一种类似双向绑定的方式,不需要我们在父组件之中写繁琐的v-on了呢。答案是有的。例如父组件有个title属性,子组件只要通过this.$emit('update:title',xxxx),父组件的title属性要加上sync修饰符,就可以实现父子组件传参的伪双向绑定,避免了繁琐的v-on带来的多余的function。具体写法如下:
<tfunc :title.sync="title"/> (父组件内,注意sync)
this.$emit('update:title', this.title + this.click) (子组件中使用emit时注意update:)
除了上面的几种方式外,组件间的传值还可以通过总线发布订阅的方式实现,它的本质还是一方监听一方触发,但他的影响范围却从父子之间扩展到了兄弟之间,甚至是页面之间(但首先要确保页面实例存在,监听的事件没有被销毁,不然一方的触发是找不到对应的执行函数的)。具体实现呢还是利用VUE天生自带的那套监听模式,你可以在根实例中监听事件,然后在对应的页面用根实例的emit去触发。写法如下:
this.$rooot.$on('doSomething', this.doSomething) (订阅方)
this.$root.$emit('doSomething',参数) (发布方)
this.$root其实就是我们一开始new出来的vue实例,一般为了减少在vue实例上绑定太多的东西,所以我们秉承着专人干专事的原则我们会重新new 一个vue专门用来做总线,就像下方的bus一样:
在使用过程中通过this.$root.Bus.$on(......) 和this.$root.Bus.$emit(.....)就可以了。像这种事件监听的订阅模式还可以通过dispatchEvent的方式广播实现,这里就不扩展了,感兴趣的朋友可以看笔者的另一篇博客广播与派发原理(基于vue实现dispatch, broadcast)。道路千万条,总有一条适合你。