寫文章不容易,點個讚唄兄弟 專一 Vue 源碼分享,文章分爲白話版和 源碼版,白話版助於理解工做原理,源碼版助於瞭解內部詳情,讓咱們一塊兒學習吧 研究基於 Vue版本 【2.5.17】app
若是你以爲排版難看,請點擊 下面連接 或者 拉到 下面關注公衆號也能夠吧dom
Vue 的自定義事件很簡單,就是使用 觀察者模式 進行事件的監聽和分發函數
Vue 封裝的這個觀察者模式,能夠說是很完善了,這個能夠獨立抽取出來的在其餘項目中使用的代碼,只須要作一點點改動,把事件存儲器換個地方(Vue 放在了實例上)學習
我常常在項目中使用,就是爲了解耦或者解決一些異步的問題this
今天來詳細探索 Vue 的 自定義事件prototype
首先,Vue 的事件存儲器放在那裏?3d
沒錯,放在 vm._events 中code
你看,好比你這樣監聽事件blog
看到實例上保存了你的事件
vm._events
看下這個事件存儲器在哪裏生成的
首先,實例在初始化的時候,給實例增長一個事件存儲器 _events
Vue.prototype._init = function(options) { initEvents(vm); //...初始化選項數據,解析模板,掛載dom等 } function initEvents(vm) { vm._events = Object.create(null); }
之後,全部這個實例監聽的事件,就都存在這裏了
那麼,接下來就來看 自定義事件的源碼了
下面的源碼比較不太屬於 Vue 的內容,比較獨立,很實用,相信你們也都看得懂,這裏主要起一個記錄的做用
綁定事件,$on
一次性綁定事件,$once
觸發事件,$emit
解綁事件,$off
註冊事件,接收 事件名和回調,很清楚了,都能看得懂
Vue.prototype.$on = function(event, fn) { var vm = this; if (Array.isArray(event)) { for (var i = 0,l = event.length; i < l; i++) { this.$on(event[i], fn); } } else { (vm._events[event] || (vm._events[event] = [])).push(fn); } // 爲了鏈式調用 return vm };
單次註冊。只監聽一次,觸發以後立刻銷燬
它妙就妙在,把回調包裝了一下,在 回調執行時,先解綁事件,再調用原回調
Vue.prototype.$once = function(event, fn) { var vm = this; function on() { vm.$off(event, on); fn.apply(vm, arguments); } on.fn = fn; vm.$on(event, on); // 爲了鏈式調用 return vm };
觸發事件,接收事件名,而後拿到本來設置的回調,遍歷調用
Vue.prototype.$emit = function(event) { var vm = this; var _events= event.toLowerCase(); var cbs = vm._events[_events]; if (cbs) { cbs = cbs.length > 1 ? toArray(cbs) : cbs; var args = toArray(arguments, 1); for (var i = 0, l = cbs.length; i < l; i++) { cbs[i].apply(vm, args); } } // 爲了鏈式調用 return vm };
取消監聽事件或者移除監聽回調
接收事件名 和 綁定時的事件回調
很簡單的啦
Vue.prototype.$off = function(event, fn) { var vm = this; if (!arguments.length) { vm._events = Object.create(null); return vm } // 遞歸調用 if (Array.isArray(event)) { for (var i = 0, l = event.length; i < l; i++) { this.$off(event[i], fn); } return vm } var cbs = vm._events[event]; if (!cbs) return vm if (!fn) { vm._events[event] = null; return vm } // 去掉特定的函數 if (fn) { var cb; var len = cbs.length; // 遍歷移除相應回調 while (len--) { cb = cbs[len]; if (cb === fn || cb.fn === fn) { cbs.splice(len, 1); break } } } // 爲了鏈式調用 return vm };