前面說了
父組件 >>>> 子組件
子組件 >>>> 父組件
父組件 <<<< 子組件
子組件 <<<< 父組件
相互傳值的方式,那兩個組件不是父子關系咋辦啊?之前面試被問到,問的我一臉懵逼,我當時回答說,localstorage、定義全局變量;
其實這兩種方式也不是不可以,但老二存了數據進去,老大怎么獲取呢?老大不知道你啥時候存進去了呀!?
面試結束我就想,移動端開發有廣播,注冊廣播,發送廣播的機制,記得瀏覽器也有這種機制,那是不是vue里邊也有相應的機制呢?
我一搜,哎呦 ~~我去~~ 真有,(妹的,之前也接觸過的,就是通過bus傳遞,沒實際在項目里邊用過,所以忘了)。
那這個bus具體咋整涅:
1.定義一個單獨的js文件,起名為:vueBus.js,然后里邊寫:
import Vue from 'vue'; let bus = new Vue(); export default bus;
2.在需要接收信息的地方注冊監聽事件:先引入bus
mounted(){ let self = this bus.$on('fromSecond',(data)=>{ self.res = data//獲取到傳過來的值 }); },
3.然后發送廣播事件:先引入bus
sendMsg(){ bus.$emit("fromSecond",this.res) }
注意:先引入bus:
import bus from '@/api/vueBus.js'
並且發一次,多個注冊過的地方都能接收到
--------------------------------------------------------------------------------------------------------------------------分割線
那實現原理是什么樣的呢?當然知道了上邊的內容完全夠用了,但在這個人人裝b的時代,你不了解原理怎么能夠呢,原理你要知道並且深深的知道!
想知道原理就得看源碼:
export function eventsMixin (Vue: Class<Component>) { const hookRE = /^hook:/ // 這里是注冊事件 Vue.prototype.$on = function (event: string | Array<string>, fn: Function): Component { const vm: Component = this if (Array.isArray(event)) {// 這里看到注冊事件時候可以傳個數組進來 for (let i = 0, l = event.length; i < l; i++) {//循環着添加事件 vm.$on(event[i], fn)//調用自己 } } else {// _events是裝所有事件的對象 // 添加進_events去 如果對應的這個事件名已經有方法了就再往里邊push一個 // 如果沒有就建一個數組往里邊push // 所以 你往數組里邊放幾次執行時候就會執行幾次, // 同一個事件名 注冊幾個不同/相同回調方法 都會執行 (vm._events[event] || (vm._events[event] = [])).push(fn) // optimize hook:event cost by using a boolean flag marked at registration // instead of a hash lookup if (hookRE.test(event)) { vm._hasHookEvent = true } } return vm } // 這里是一次性綁定 Vue.prototype.$once = function (event: string, fn: Function): Component { const vm: Component = this // 封裝一個fn 執行時候移除綁定 這樣就實現只能觸發一次了 function on () { vm.$off(event, on) fn.apply(vm, arguments) } on.fn = fn vm.$on(event, on)// 綁定封裝過一次的fn return vm } // 移除綁定事件 Vue.prototype.$off = function (event?: string | Array<string>, fn?: Function): Component { const vm: Component = this // all if (!arguments.length) { vm._events = Object.create(null) return vm } // array of events // 是數組的話 循環着去解綁 if (Array.isArray(event)) { for (let i = 0, l = event.length; i < l; i++) { vm.$off(event[i], fn) } return vm } // specific event const cbs = vm._events[event] if (!cbs) {// 找不到這個事件 你解綁個p呀 return vm } if (!fn) {// 解綁時候沒有傳過來 回調方法 你解綁個p呀 vm._events[event] = null return vm } // specific handler let cb let i = cbs.length while (i--) { cb = cbs[i] if (cb === fn || cb.fn === fn) { cbs.splice(i, 1)//找到他 並且移除他 break } } return vm } // 發送事件 Vue.prototype.$emit = function (event: string): Component { const vm: Component = this if (process.env.NODE_ENV !== 'production') { const lowerCaseEvent = event.toLowerCase() if (lowerCaseEvent !== event && vm._events[lowerCaseEvent]) { tip( `Event "${lowerCaseEvent}" is emitted in component ` + `${formatComponentName(vm)} but the handler is registered for "${event}". ` + `Note that HTML attributes are case-insensitive and you cannot use ` + `v-on to listen to camelCase events when using in-DOM templates. ` + `You should probably use "${hyphenate(event)}" instead of "${event}".` ) } } // 找到這個方法 let cbs = vm._events[event] if (cbs) { cbs = cbs.length > 1 ? toArray(cbs) : cbs
// 把傳過來的參數去掉第一個,轉化成參數數組
const args = toArray(arguments, 1) const info = `event handler for "${event}"` for (let i = 0, l = cbs.length; i < l; i++) { // 執行這些方法 invokeWithErrorHandling(cbs[i], vm, args, vm, info) } } return vm } }
可以看到:添加事件時候會把方法放到Vue的_events對象上,這個對象的key是事件名,value是fn數組;你調用$emit時候,他會根據你傳過來的事件名稱去找到對應的fn數組,循環執行之。
看源碼意外收獲:
1. 注冊事件時候事件名而已傳數組
2. 一個/組事件名可以綁定不同的方法,執行時候按順序一並執行
3. 解除事件監聽時候需要傳事件名和方法進來
4. 注冊一次性事件是怎么完成的
over!