首先介紹一下什么叫單項數據流?
我們都知道在Vue中組件之間是單項數據流的,規定子組件不可以直接訪問父組件的數據,只能通過props屬性讓父組件把數據傳遞給子組件。並且子組件不可以直接修改父組件傳遞給子組件的數據。(props是只讀的)
有了大概了解之后直接上代碼看看怎么使用。

1 <div id="app"> 2 <h2>{{age}}</h2> 3 <my-label v-bind:a="age" b="hello" :c="age >= 18?'已成年':'未成年'"/> 4 </div> 5 6 <script> 7 new Vue({ 8 el: '#app', 9 data: { 10 age: 15 11 }, 12 components: { 13 'my-label': { 14 // 給當前my-label設置了三個自定義屬性a b c 15 // 組件組件自身就可以通過 this.props.a 16 props: ['a','b','c'], 17 data: () => ({ 18 name: 'my-label' 19 }), 20 template: ` 21 <div> 22 <p>a:{{a}}</p> 23 <p>b:{{b}}</p> 24 <p>c:{{c}}</p> 25 <child-a v-bind:name="name" :rootage="a"></child-a> 26 </div> 27 `, 28 components: { 29 'child-a': { 30 props: ['name','rootage'], 31 template: '<div>我是childA 父組件的name為{{name}} rootAge{{rootage}}</div>' 32 } 33 } 34 } 35 } 36 }) 37 </script>
通過上述代碼我們可以知道new Vue() 就是父組件,data中的age通過props傳遞到子組件中,使用的使用就要通過v-bind(縮寫就是一個冒號 : )綁定到組件標簽上。這樣就可以拿到父組件里的數據。但是數據是只讀的,不能直接修改。那這時候就有個疑問,我們又想修改呢?那該怎么辦,對吧!所以就有了反向傳值這個概念。那接下來我們看看什么是反向傳值!!!
反向傳值
- 語法: $emit('自定義事件名', 向父組件回調函數傳遞參數)
- 注意: $emit只接受兩個參數:參數一觸發父組件監聽的指定事件名(自定義),參數二(可選)向父組件監聽事件回調函數傳遞的參數
接下來模擬一個情景: 兒子問父親拿生活費,原本一個月的生活費是1500,之后發現1500不夠花了。這時候是不是得打電話給父親說我零花錢不夠了,要加錢,父親才能給你。對吧!那這時候是怎么一個過程呢?首先不夠了是不是得問父親拿,那你不能直接拿呀,錢是父親的不是你的,問了之后他才給你對不對。嚴格意義上不問直接拿就是偷錢了,當然你們關系好經常這樣干那也沒辦法啊!然后通過什么方式呢?是不是有現金,微信轉賬,支付寶轉賬等方式呀!那父親肯定是包含兒子的吧,你有什么事就給我綁定什么事件,在這個情景的話就比如說生活費不夠花了這個事件,父親接到你的電話說不夠了才能做下一步操作。
那么換成代碼 $emit() 就幫我們干了這件事,通過一個手段來變更數據。重點來了,那我們既然有了這個情景,就換成代碼來實現。

1 <div id="father"> 2 <child :zfb="money" @call="callHandel"/> 3 </div> 4 5 <script> 6 // 父組件通過props傳遞的值都是只讀屬性,而不能直接修改的 7 // vue子組件不能修改父組件props的值,否則在控制台中會報錯 8 // 不能在子組件內部修改props中zfb的值 9 new Vue({ 10 el:'#father', 11 data: { 12 money: 1500 13 }, 14 methods: { 15 // 調用方法 (加生活費的操作) 16 callHandel(m) { 17 // m 在原本的基礎上加多少 18 console.log('兒子給我打電話了,說錢不夠花') 19 this.money += m 20 } 21 }, 22 components: { 23 'child': { 24 props: ['zfb'], 25 template: ` 26 <div> 27 父親通過支付寶轉生活費額度:{{zfb}} 28 <button @click="$emit('call',800)">向父親打電話</button> 29 </div> 30 ` 31 }, 32 33 } 34 }) 35 </script>
圖示:
結果:通過截圖我們可以知道,點一次就加800,$emit()幫我們實現了反向修改原本props傳遞過來的1500變成了2300
解析:可能會有疑問:$emit('call',800)" 中的800去哪里了呢? 這里的800就是上面說的$emit()的第二個參數,我們在子組件里使用了$emit()綁定了一個叫call的事件。換成剛剛那個情景的意思就是你是不是得打電話告訴父親說生活費不夠了,父親接收到了才能給你加吧!所以在使用的時候 <child :zfb="money" @call="callHandel"/> @call綁定了就調用callHandel方法進行給你加生活費的操作。最后你就拿到了新的生活費,整個流程就結束了。
那這時候又有小伙伴問了你憑什么就說$emit()可以改,而props傳過來的不能直接改?那我們來個對比!只需修改一部分代碼就可以看出效果!
那我們修改一下template里的代碼
template: ` <div> 父親通過支付寶轉生活費額度:{{zfb}} <button @click="zfb+=800">向父親打電話</button> // 更改之前 <button @click="$emit('call',800)">向父親打電話</button>
</div> `
當我點擊按鈕的時候出現了以下報錯信息
大概意思是:通過props傳遞給子組件的zfb,不能在子組件內部修改props中的zfb值。那我們就知道不能更改 prop使其和父組件同步 , 而是讓應該這個組件提交個事件給父組件,可以用 watch 變量,如果變量發生改變就$emit()事件。
總結:通過對比我們就得出了當我們需要雙向改變值的時候就要用到反向傳值。有一個很經典的例子叫todo列表(在這就先不列舉了...哈哈哈~),這個案例中修改todo內容等等一系列操作就用到了反向傳值。