摘要:可歸根結底,所謂事件監聽,通常都是一個需要預處理的過程,即在你初始化你的實例時就需要去為其注冊監聽。當然既然用了,更好的利用給我們帶來的遍歷也很重要,所以對于這種很少會出現的麻煩,我們有一個預期,并可以快速定位并修復問題,就可以了
由于新工作需要用vue,所以最近接觸最多的也是vue,因為之前一直在用react,所以對于vue上手還是很快的。
我也盡量找一些他們兩個的異同點,除了多了一些輔助用的方法以外,最大的不同應該是對于組件間的通信,不僅有props,還有一種事件監聽,也是可以通過組件間傳遞的。
我們知道vue的事件監聽是一個很方便的設計,代碼上一目了然,而且給我們增加了多種修飾符(雖然我都沒怎么用過)來簡化你的代碼??蓺w根結底,所謂事件監聽,通常都是一個需要預處理的過程,即在你初始化你的實例時就需要去為其注冊監聽。這當然沒什么不好,我們之所以需要做事件監聽,就是為了當我們因為業務復雜而形成一個事件需要觸發多處回調時,我們可以通過這種注冊監聽機制更好的管理他們。這一點上他比react做的更好,如果在react里我們需要中途給某個事件添加更多回調,我們需要手動維護更多的代碼(這里說的事件監聽機制單指vue的v-on方式,react本身dom上的事件是通過事件代理方式統一在document身上并通過每個dom的唯一id來維護一個hashMap來實現的)。
但是,在vue2.+中,vue引入了diff算法和虛擬dom來提升效率。我們知道這些事為了處理頻繁更新dom元素所提出的一種優化方案,可頻繁變動更新以及事件監聽的初始化之間是否會有矛盾,當組件需要變動時,有沒有對注冊過的事件進行解綁? 我們來寫一些簡單的代碼印證一下。
我們寫兩個div做的按鈕,一個是寫的html代碼,一個是通過組件的形式插入,兩個按鈕完全一樣,但我們加一個disabled的屬性在外層,并通過if-else來判斷disabled從而顯示不同的按鈕(當然正常場景下我們不會這么去寫代碼,這里只是通過這種方式模擬一種特殊場景,我們自行考慮在我們的業務中是否存在這種場景)。
可點擊不可點擊
我們加一點樣式,讓他盡量好看一點,看著很簡單,兩個按鈕,可點擊時為他綁定一個點擊事件,不可點擊時不為他綁定。不同點是一個是直接寫的html代碼,一個是組件。組件的代碼如下:
然后在mounted周期里加一個1秒的settimeout將disabled變為false,然后我們測試一下
(靠!傳圖片失敗····· 我們用語言描述吧)
總之就是,當disabled還是true得時候,兩個按鈕點擊都會彈出可點擊的alert。但當disebled變為false的時候,上面用html寫的不會再彈框,可下面用組件寫的就還是會彈窗。
這種問題出現時是非常不好定位的,因為代碼上很顯然不會去調取這個clicktest事件,而在頁面上,我們也能確定按鈕已經變為不可點擊的那一個了。那為什么這個事件還是會被調取呢?
這先要從diff算法說起,傳統的diff tree算法的算法復雜度是O(n^3),而react在引入diff算法時,拋除了跨級移動的情況,即只比對同一級的節點異同,讓算法復雜度降低到了O(n),讓我們可以肆無忌憚(當然也要適可而止)的頻繁刷新整個頁面。
(呵呵,沒圖)
diff有一條策略是擁有相同類的兩個組件將會生成相似的樹形結構,擁有不同類的兩個組件將會生成不同的樹形結構。所以它的比對順序就是
1)tree diff
2)component diff
3)element diff
回到我們的代碼上,我們在進行component diff時,認為他們是相同的組件,然后進行element diff,即進行新增 刪除和移動
所以問題就是發生在了這里,在實例化組件的時候我們初始化了事件監聽,但在替換相同組件里的dom時,vue并沒有對已添加到組件上的事件監聽做刪除。
我們看一下vue的代碼,
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) for (let i = 0, l = cbs.length; i < l; i++) { try { cbs[i].apply(vm, args) } catch (e) { handleError(e, vm, `event handler for "${event}"`) } } } return vm }
vue是通過vdom里的_events屬性下確定是否有綁定事件的。我們看一下不可點擊的按鈕的_events
: clickTest : Array(1) 0 : ? invoker() length : 1
發現clicktest還在。這就是問題所在了。
那么我們該如何去回避這樣的問題呢,還是應從diff的比對方式來解決問題,還是看代碼。
function sameVnode (a, b) { return ( a.key === b.key && ( ( a.tag === b.tag && a.isComment === b.isComment && isDef(a.data) === isDef(b.data) && sameInputType(a, b) ) || ( isTrue(a.isAsyncPlaceholder) && a.asyncFactory === b.asyncFactory && isUndef(b.asyncFactory.error) ) ) ) }
也就是對diff來說,所謂相同的第一判定原則是key。
key也是react引入diff時添加的一個屬性,用來判斷前后vdom樹上是否為統一元素(注意是同級關系上),所以我們只需要在代碼上加key,就可以避免這個問題
這樣,我們在點擊按鈕時,就不會再出彈框了。
key的作用很廣泛,當我們在遍歷數組生成dom時,添加一個可確定的唯一id(注意不應該用數組索引),會優化我們的比對效率以及更少的操作dom。我們也會在某個div上添加key以確保他不會因為兄弟元素的變動而被重新渲染(這類div一般會被綁定react或vue意外的事件或動作,如在這個div中生成了一個canvas等)。
那么除了在組件上加這種不必要key值以外,還有別的方法解決嗎?
有的,這里有一種很反vue但是類react的方式,就是把回調事件通過props的方式傳遞,向下面著這樣,
props: { "clickTest": { type: Function } }, methods: { handleClick() { //this.$emit("clickTest") this.clickTest && this.clickTest() } }
雖然vue給了我們更方便的事件傳遞的方式,但props里是允許我們去傳遞任何類型的,我的期望是在真實的dom上或者在公共組件的入口處以外的地方,都是通過props的方式來傳遞結果的。雖然這種方式很不vue,而且也享受不到v-on給我們帶來的遍歷,但是這樣確實可以減少不必要的麻煩。
當然既然用了vue,更好的利用vue給我們帶來的遍歷也很重要,所以對于這種很少會出現的麻煩,我們有一個預期,并可以快速定位并修復問題,就可以了
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/90439.html
摘要:回調函數會接收所有傳入事件觸發函數的額外參數。這種方式類似于中的監聽事件和觸發事件如果不是上面這種方法指定的鉤子函數,就需要執行源碼上半部分的代碼邏輯。 上篇文章中,我們主要講了initLiftcycle方法,它的作用是初始化vm實例中和生命周期相關的屬性。今天為大家介紹另一個方法——initEvents。從這個方法的名稱來看,我們知道它是和事件相關的方法,具體怎么相關,我們先來看源碼...
摘要:不必擔心,因為所有的事件處理方法和表達式都嚴格綁定在當前視圖的上,它不會導致任何維護上的困難。當一個被銷毀時,所有的事件處理器都會自動被刪除。 Vue.js的事件處理 監聽事件 我們可以用 v-on 指令監聽 DOM 事件來觸發一些 JavaScript 代碼。 {{msg}} var vm = new Vue({ el:.box, data:{ ...
摘要:輸入框首尾清除空格在中監聽鍵盤事件移動端底部被彈出的鍵盤遮擋輸入框是通過一直放在頁面底部,當點擊進行輸入的時候,就會出現如下圖片情況有的機型會遮擋一些。 前言 input是我們接受來自用戶的數據常用標簽,在前端開發中,相信每個人都會用到這個標簽,所以在開發過程中也時候也會遇到一些問題,本文的內容是我在跟input相愛相殺過程中產生的,在此記錄分享一下。如果喜歡的話可以點波贊/關注,支持...
摘要:輸入框首尾清除空格在中監聽鍵盤事件移動端底部被彈出的鍵盤遮擋輸入框是通過一直放在頁面底部,當點擊進行輸入的時候,就會出現如下圖片情況有的機型會遮擋一些。 前言 input是我們接受來自用戶的數據常用標簽,在前端開發中,相信每個人都會用到這個標簽,所以在開發過程中也時候也會遇到一些問題,本文的內容是我在跟input相愛相殺過程中產生的,在此記錄分享一下。如果喜歡的話可以點波贊/關注,支持...
閱讀 2190·2021-11-24 09:38
閱讀 3251·2021-11-08 13:27
閱讀 3091·2021-09-10 10:51
閱讀 3160·2019-08-29 12:20
閱讀 673·2019-08-28 18:28
閱讀 3467·2019-08-26 11:53
閱讀 2715·2019-08-26 11:46
閱讀 1525·2019-08-26 10:56