在表单输入组件节点上使用v-model
el: "#app-1",data() { return { text: '' }},components: { 'component-1': { template: '' }}
直接在表单组件的标签上使用v-model
,没有任何作用。
首先看看v-model
如何在原生input
元素上工作的。
data() { return { text: '' }}
上面的标签为v-model
指令的完全形式,v-model
是下面代码的语法糖,通过事件处理函数与数据绑定完成双向绑定。需要注意的是事件处理函数的内容是默认的。
v-bind:value="somedata"v-on:input="somedata = $event.target.value"
对比原生input
元素,可以得出,输入组件上的v-model
也是由事件处理与数据绑定组成。
data() { return { text_1: '', text_2: '' }}//......'component-2-1': { template: '', props: ['value'], methods: { eventHandler: function (event) { this.$emit('input', event.target.value) } }}
输入组件模板中,监听每次输入,每次输入时就触发input
自定义事件,并以元素的值作为该事件负载;在父组件上监听这个input
自定义事件,触发该事件时执行默认的方法,将事件负载传递给父组件自己的data
;这里要关注默认的处理方法somedata = arguments[0]
,这个arguments[0]
即是自定义事件的负载;若父组件数据修改了,会通过:value=props
下发给子组件,因此子组件需要定义一个Propsvalue
,子组件还要将value
绑定在自己的input
元素上,向其他使用该组件的地方传递变化。
变化的过程: 输入 - 子组件触发自定义事件 - 父组件监听到事件 - 根据负载修改数据 - 将修改数据下发给组件 - 引起其他位置的变化
对于子组件模板上,绑定自己原生input
事件的方式,以上使用一个eventHandler
事件处理方法。我们也可以在没有参数的v-on
上绑定一个事件对象(在computed
中定义),如下:
'component-2-2': { template: '', props: ['value'], computed: { eventDict: function () { return { input: event => this.$emit('input', event.target.value) } } }}
子组件props.value
用来传递变化给其他使用该输入组件的地方,如果没有它,只是单向的绑定。
使用$attrs、$listeners在多层次的组件结构中实现通信
如果在多层组件结构中,只是单纯的数据通信,那么使用$attrs、$listeners是最方便的。
在使用它们之前,先来看个选项inheritAttrs
。 data(){ return { foo:'foo', bar:'bar' }},components:{ 'component-3':{ props:['foo'], template:'{ {foo}}', }}
在父组件中,我们绑定了两个Props,foo
和bar
,下发数据给子组件时,子组件只定义了foo
,bar
没地方传,组件渲染时它就留在了子组件的根元素上。
如果我们不想在子组件根元素上保留这个属性,我们可以设置选项inheritAttrs
为false。
inheritAttrs:false
使用$attrs
、$listeners
。
new Vue({ el: '#app-4', data() { return { firstData: 'firstData', secondData: 'secondData', thirdData: 'thirdData', fourthData: 'fourthData' } }, methods: { firstEvent: function () { console.log('第一层组件触发的事件') }, secondEvent: function () { console.log('第二层组件触发的事件') }, thirdEvent:function(){ console.log('第三层组件触发的事件') }, fourthEvent:function(){ console.log('第四层组件触发的事件') } }, components: { 'first': { 'props': ['firstData'], template: '', mounted() { this.$emit('first'); }, inheritAttrs:false, components:{ 'second':{ 'props':['secondData'], template:'{ {firstData}}
', mounted() { this.$emit('second'); }, methods: { eventHandler(payload){ $(this.$el).find('h2').after('' + payload + '') } }, inheritAttrs:false, components:{ 'third':{ 'props':['thirdData'], template:'{ {secondData}}
', mounted() { this.$emit('third'); }, methods: { eventHandler(payload){ $(this.$el).find('h3').after('' + payload + '') } }, inheritAttrs:false, components:{ 'fourth':{ 'props':['fourthData'], template:'{ {thirdData}}
', mounted() { this.$emit('fourth','负载在第四层上事件上的数据'); } } } } } } } } }}){ {fourthData}}
$listeners
上保存着所有低层组件触发的自定义事件,使用v-on="$listeners"
将本层及以下层触发的事件传递给上一层,上一层中可以监听$listeners
对象中所有的事件。整个过程即一个事件广播的形态。
$attrs
上保存着所有未被下发的上层组件中的数据,各层组件使用v-bind="$attrs"
向下层组件传递下发的数据。$attrs
好像一块数据蛋糕,被高层组件拿走的部分,低层组件无法再使用。配合inheritAttrs:false
可以使那些用不到的数据属性,不会保留在组件的根元素上。
v-on="$listeners"
向上广播事件,v-bind="$attrs"
向下下发数据
父子组件的引用
示例结果是这样:
- 使用$parent和$refs在父子组件间传递数据
- { {message}}
{ {message}}
new Vue({ el:'#app-2', data:{ message:'' }, methods:{ handle(){ this.$refs.child.message = this.message } }, components:{ 'component-21':{ template:'', data(){ return {message:''} }, methods:{ handle(){ this.$parent.message = this.message } } } }, })
在子组件中可以使用$parent
获取父组件数据,也可以修改它。
$refs
获取子组件数据,也可以修改,之前必须在视图节点上给子组件取一个名字如:ref="child"
(见)。就靠这两个属性,进行父子组件间的通信。 事件管理器Bus
通过该方法不仅可在任意组件内进行通信。创建一个全局的实例bus管理事件触发和监听
var bus = new Vue()
之后创建表单输入组件,在其输入事件中触发bus上的message
事件,在created
钩子中监听该事件。
Vue.component('component-a', { template:``, data(){ return {message:''} }, methods:{ emitEvent(){ bus.$emit('message',this.message) } }, created(){ var _this = this bus.$on('message', c_msg => { _this.message = c_msg }) }})Vue.component('component-b', { template:``, data(){ return {message:''} }, methods:{ emitEvent(){ bus.$emit('message',this.message) } }, created(){ var _this = this bus.$on('message', c_msg => { _this.message = c_msg }) } })new Vue({ data:{ message:'' }, methods:{ emitEvent(){ bus.$emit('message',this.message) } }, created(){ var _this = this bus.$on('message', c_msg => { _this.message = c_msg }) }}).$mount('#app-1')
- 使用全局View实例管理事件,在任意组件间传递数据
- { {message}}
-
{ {message}} -
{ {message}}
当一个组件输入时触发bus上的事件,所有组件都会监听到,并使用事件上的负载数据。