vue的两大特点就是
响应式原理
和组件系统
,今天我们就来看一下关于组件系统
中各级组件是如何传递数据的。
之后的演示都在如下图的关系网中进行:
Father
对于Son
和Daughter
来说为父组件。
Son
对于Father
为子组件,但是对于
GrandSonBySon
和GrandDaughterBySon
为父组件。
Daughter
对于Father
为子组件,但是对于
GrandSonByDau
为父组件。
同时,Daughter
和Son
有同一个父组件
Father
因此这两个组件互为兄弟组件
父组件通过子组件暴露的prop的sonValue、dauValue向子组件传值。
//父组件 <template> <div class="father"> <son v-bind:sonValue="son"></son> <daughter v-bind:dauValue="daughter"></daughter> </div> </template> <script> import Daughter from '@/views/Daughter.vue' import Son from '@/views/Son.vue' export default { components: { Daughter, Son }, data () { return { son:'son', daughter:'daughter' } } } </script> 复制代码
子组件通过内部定义的props接收到父组件的值,每当父组件的值变化的时候,子组件内部的值就会发生变化,可以使用this.[props]
来进行引用。
// 子组件 <template> <div class="father"> {{sonValue}} <!-- son --> </div> </template> <script> export default { props:{ sonValue:{ type:String, default:"" } }, data () { return { } }, mounted(){ console.log(this.sonValue) // 父组件输入的值'son' } } </script> 复制代码
子组件向父组件传值其实就是触发事件
然后通过事件传参返回给父组件。
// 子组件中触发 this.$emit('事件名','值') 复制代码
// 父组件中将事件绑定回父实例的函数上,第一个参数为子组件的传值。 <son v-bind:sonValue="son" @changeFather="change"></son> methods:{ change(value){ //value是子组件的传过来的值 this.fromSon = value } } 复制代码
注意:Vue 单向数据流 的原因所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定 父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。额外的,每次父级组件发生更新时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。
// 子组件son的修改 data () { return { //初始化一个子组件内部的值,将传入的值赋值。 realSonValue:this.sonValue } }, 复制代码
vm.$ref.[child]
和vm.$parent
(父子组件之间的显示调用)vm.$ref.[child]
在父组件中引入子组件并且设置子组件的ref值。ref="son"
// 父组件 <template> <div class="father"> <son v-bind:sonValue="son" @changeFather="change" ref="son"></son> {{fromSon}} <daughter v-bind:dauValue="daughter"></daughter> </div> </template> // 父组件都可以通过引用'ref'的方式来获得。 mounted(){ console.log(this.$refs.son.innerValue) // '$refs.son' this.$refs.son.innerFun() // 'I am Son' } 复制代码
子组件内部定义的一个值、或者方法。
data () { return { // 初始化一个子组件 innerValue:'$refs.son' } }, methods:{ innerFun(){ console.log('I am Son') } }, 复制代码
vm.$parent
在子组件中引入父组件实例
// 父组件 data () { return { innerFather:'vm.$parent' } }, 复制代码
// 子组件 mounted(){ console.log(this.$parent.innerFather) // ·'vm.$parent' } 复制代码
注意:有的小伙伴会发现一个问题就是既然有
vm.$parent
那应该也有vm.$children
结果一试还真有,但是在vm中你会发现,一个父组件可以有很多个子组件,也就是有vm.$children
使用这个打印出来的是一个子组件的类数组,无法定位到你要子组件的值,但是每个子组件只有一个父组件这个是固定的。
通过vm.$emit
和vm.$on
通过 vm.$emit
触发当前实例上的事件,并将参数传递给监听器,通过 vm.$on
监听当前实例上的自定义事件。
简单理解为一个托管平台,这个平台必须独立于关联两个组件之外的一个vue实例,当两个组件需要传递数据的时候,发起方在平台上触发一个
vm.$emit
然后传递参数,接收方在平台设置一个监听vm.$on
接收参数并且执行方法,方法的参数就是发送方传递过来的参数。
先定义一个托管平台,vue项目中你可以以单文件的形式在各个兄弟组件中引入,或者像我一样生成一个vue实例挂在整个Vue实例之下。
var bus = new Vue() Vue.prototype.bus = bus 复制代码
// 发送方兄弟组件触发$emit设置名字以及传递参数 mounted(){ this.bus.$emit('SonByDauChange','I am grandSonByDau') } 复制代码
// 接收方兄弟组件监听事件,并执行回调 methods:{ brotherData(value){ console.log(value) // 'I am grandSonByDau' } }, mounted(){ this.bus.$on('SonByDauChange',this.brotherData) } 复制代码
vm.$attrs
和vm.$listeners
props和emit的父子组件传值的进化版。可以从父组件到孙组件的跨级传递,中间的子组件作为
vm.$attrs
和vm.$listeners
的实现载体
vm.$attrs
存放了没有被子组件props所标记的属性,即可以理解为父组件在子组件上自己绑定的数据
// 父组件引入son子组件 <div>这是父组件</div> <Son :myData="value" :grandSon="grandSon" @changeGrandSon="changeGrandSon"></Son> data () { return { myData:'from props', grandSon:'from father' }; }, methods: { changeGrandSon(value){ console.log(value) // from grandSon } }, // 子组件,能在父组件中被引用的是myData的值,但是我们能通过vm.$attr对象获得props之外的属性 <div>这是子组件</div> <grandSon v-bind="$attrs" v-on="$listeners"></grandSon> // 划重点可以将父组件给予子组件prop的额外的值传递给孙组件 props:{ myData:{ type:String, default:'' } mounted() { console.log(this.$attrs.granSon) //可以获得from father }, // 孙组件,不需要props可以直接获得从父组件传递过来的值 <div>这是孙组件</div> <div>{{$attrs.grandSon}}</div> // from father mounted() { console.log(this.$attrs) this.$emit('changeGrandSon','from grandSon') // 触发父组件的changeGrandSon的事件 }, 复制代码
总结:vm.$attrs
和vm.$listeners
都可以实现从父组件将值和事件跨级(上下游,兄弟组件不可)传递,但是在中间的组件(子组件)需v-bind="$attrs" v-on="$listeners"
要绑定传值。
这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效。只有数据向下传递,并没有事件响应向上。可以向下跨级传递,子、孙只要有inject均可。
// 父级组件提供 'foo' var Provider = { provide: { foo: 'bar' }, // ... } // 子、孙组件注入 'foo' var Child = { inject: ['foo'], created () { console.log(this.foo) // => "bar" } // ... } 复制代码
单纯提出想法,中间可能有坑更多的实际操作需要各位亲自去尝试,结合api文档。