code_v-model、$attrs、$listenersjavascript
el: "#app-1", data() { return { text: '' } }, components: { 'component-1': { template: '<input type="text">' } }
<component-1 v-model="text"></component-1>
直接在表單組件的標籤上使用v-model
,沒有任何做用。vue
首先看看v-model
如何在原生input
元素上工做的。java
data() { return { text: '' } }
<input type="text" v-bind:value="text" v-on:input="text = $event.target.value">
上面的標籤爲v-model
指令的徹底形式,v-model
是下面代碼的語法糖,經過事件處理函數與數據綁定完成雙向綁定。須要注意的是事件處理函數的內容是默認的。git
v-bind:value="somedata" v-on:input="somedata = $event.target.value"
對比原生input
元素,能夠得出,輸入組件上的v-model
也是由事件處理與數據綁定組成。segmentfault
data() { return { text_1: '', text_2: '' } } //...... 'component-2-1': { template: '<input type="text" v-bind:value="value" v-on:input="eventHandler">', props: ['value'], methods: { eventHandler: function (event) { this.$emit('input', event.target.value) } } }
<component-2-1 v-model="text_1"></component-2-1> <component-2-1 v-bind:value="text_1" v-on:input="text_1 = arguments[0]"></component-2-1>
輸入組件模板中,監聽每次輸入,每次輸入時就觸發input
自定義事件,並以元素的值做爲該事件負載;在父組件上監聽這個input
自定義事件,觸發該事件時執行默認的方法,將事件負載傳遞給父組件本身的data
;這裏要關注默認的處理方法somedata = arguments[0]
,這個arguments[0]
便是自定義事件的負載;若父組件數據修改了,會經過:value=props
下發給子組件,所以子組件須要定義一個Propsvalue
,子組件還要將value
綁定在本身的input
元素上,向其餘使用該組件的地方傳遞變化。app
變化的過程: 輸入 - 子組件觸發自定義事件 - 父組件監聽到事件 - 根據負載修改數據 - 將修改數據下發給組件 - 引發其餘位置的變化
對於子組件模板上,綁定本身原生input
事件的方式,以上使用一個eventHandler
事件處理方法。咱們也能夠在沒有參數的v-on
上綁定一個事件對象(在computed
中定義),以下:函數
'component-2-2': { template: '<input type="text" v-bind:value="value" v-on="eventDict">', props: ['value'], computed: { eventDict: function () { return { input: event => this.$emit('input', event.target.value) } } } }
<component-2-2 v-model="text_2"></component-2-2> <component-2-2 v-bind:value="text_2" v-on:input="text_2 = arguments[0]"></component-2-2>
子組件props.value
用來傳遞變化給其餘使用該輸入組件的地方,若是沒有它,只是單向的綁定。this
若是在多層組件結構中,只是單純的數據通訊,那麼使用$attrs、$listeners是最方便的。
在使用它們以前,先來看個選項inheritAttrs
。spa
data(){ return { foo:'foo', bar:'bar' } }, components:{ 'component-3':{ props:['foo'], template:'<span>{{foo}}</span>', } }
<dd><component-3 :foo="foo" :bar="bar"></component-3></dd>
在父組件中,咱們綁定了兩個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: '<div><h1>{{firstData}}</h1><second v-bind="$attrs" v-on="$listeners"></second></div>', mounted() { this.$emit('first'); }, inheritAttrs:false, components:{ 'second':{ 'props':['secondData'], template:'<div><h2>{{secondData}}</h2><third v-bind="$attrs" @fourth="eventHandler" v-on="$listeners"></third></div>', mounted() { this.$emit('second'); }, methods: { eventHandler(payload){ $(this.$el).find('h2').after('<span>' + payload + '</span>') } }, inheritAttrs:false, components:{ 'third':{ 'props':['thirdData'], template:'<div><h3>{{thirdData}}</h3><fourth v-bind="$attrs" @fourth="eventHandler" v-on="$listeners"></fourth></div>', mounted() { this.$emit('third'); }, methods: { eventHandler(payload){ $(this.$el).find('h3').after('<span>' + payload + '</span>') } }, inheritAttrs:false, components:{ 'fourth':{ 'props':['fourthData'], template:'<div style="background: #9FEF4E;"><h4>{{fourthData}}</h4></div>', mounted() { this.$emit('fourth','負載在第四層上事件上的數據'); } } } } } } } } } })
<first :first-data="firstData" :second-data="secondData" :third-data="thirdData" :fourth-data="fourthData" @first="firstEvent" @second="secondEvent" @third="thirdEvent" @fourth="fourthEvent"></first>
$listeners
上保存着全部低層組件觸發的自定義事件,使用v-on="$listeners"
將本層及如下層觸發的事件傳遞給上一層,上一層中能夠監聽$listeners
對象中全部的事件。整個過程即一個事件廣播的形態。
$attrs
上保存着全部未被下發的上層組件中的數據,各層組件使用v-bind="$attrs"
向下層組件傳遞下發的數據。$attrs
好像一塊數據蛋糕,被高層組件拿走的部分,低層組件沒法再使用。配合inheritAttrs:false
可使那些用不到的數據屬性,不會保留在組件的根元素上。
v-on="$listeners"
向上廣播事件,v-bind="$attrs"
向下下發數據
完整代碼
示例結果是這樣:
<div id="app-2"> <dl> <dt class="first-dt">使用$parent和$refs在父子組件間傳遞數據</dt> <dd> <label>父組件</label> <input type="text" v-model="message" @input="handle"> <span>{{message}}</span> </dd> <dd> <label>子組件</label> <component-21 ref="child"></component-21> <span>{{message}}</span> </dd> </dl> </div>
new Vue({ el:'#app-2', data:{ message:'' }, methods:{ handle(){ this.$refs.child.message = this.message } }, components:{ 'component-21':{ template:'<input type="text" v-model="message" @input="handle"/>', data(){ return {message:''} }, methods:{ handle(){ this.$parent.message = this.message } } } }, })
在子組件中可使用$parent
獲取父組件數據,也能夠修改它。
在父組件中可使用$refs
獲取子組件數據,也能夠修改,以前必須在視圖節點上給子組件取一個名字如:ref="child"
(見組件引用 —— ref、$refs)。
就靠這兩個屬性,進行父子組件間的通訊。
經過該方法不只可在任意組件內進行通訊。建立一個全局的實例bus管理事件觸發和監聽
var bus = new Vue()
以後建立表單輸入組件,在其輸入事件中觸發bus上的message
事件,在created
鉤子中監聽該事件。
Vue.component('component-a', { template:`<input type="text" v-model="message" @input="emitEvent"/>`, 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:`<input type="text" v-model="message" @input="emitEvent"/>`, 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')
<dl> <dt class="first-dt">使用全局View實例管理事件,在任意組件間傳遞數據</dt> <dd> <label>父組件</label> <input type="text" v-model="message" @input="emitEvent"> <span>{{message}}</span> </dd> <dd> <label>子組件a</label> <component-a></component-a> <span>{{message}}</span> </dd> <dd> <label>子組件b</label> <component-b></component-b> <span>{{message}}</span> </dd> </dl>
當一個組件輸入時觸發bus上的事件,全部組件都會監聽到,並使用事件上的負載數據。