上一篇介紹了lightning component events的簡單介紹。此篇針對上一篇進行深入,主要講的內容為component event中的階段(Phase)。
一. 階段(Phase)的概念
lightning對於 component event提供了兩種Phase方式,Capture(捕獲階段)以及Bubble(冒泡階段)。這兩種方式和javascript中針對事件處理的Capture以及Bubble很相似。先以javascript中的針對DOM結構事件監聽進行描述。
以一個demo進行講解。
<html> <body> <div id="sampleDivId"> <a id="sampleAId"> <span id="sampleSpanId"> test event phase </span> </a> </div> </body> <script> function clickHandler(e) { console.log(e.currentTarget.tagName); } //第三個參數為 true/false. true代表 capture 方式,false代表bubble方式,默認為false document.getElementById('sampleSpanId').addEventListener('click',clickHandler); //document.getElementById('sampleDivId').addEventListener('click',clickHandler);這種方式和下面方式等同,默認為bubble document.getElementById('sampleDivId').addEventListener('click',clickHandler,false); document.getElementById(sampleAId).getEventListener('click',clickHandler,false); </script> </html>
當我們點擊 test event phase 時,因為span,a,div都有事件綁定,所以會執行三個事件,那順序應該如何呢?首先先引入兩個概念:
1. target: 實際觸發者,即設置事件的元素,此處為span元素;
2. currentTarget: 當前觸發事件的元素,即當前在執行事件的元素。
針對包含多個元素的執行順序,首先先要知道DOM結構中的事件傳播方式。DOM中針對事件的傳播有三個階段:
1. capture(捕獲階段):從根元素到事件目標元素(不算目標元素)從上到下,例子中為 document -> body -> div -> a
2. target(事件目標階段):目標元素,例子中為 span
3. bubble(冒泡階段)從目標元素(不算目標元素)到根元素從下到上,例子中為 a -> div -> body -> document
針對每個事件來說, 傳播的順序為 capture -> target -> bubble , 例子中為 document -> body -> div -> a -> span -> a -> div -> body -> document
通過傳播順序我們可以看到,除了事件源,其他元素在傳播的時候都會經歷兩次,但針對其事件僅會調用一次,所以這就是 事件綁定時需要聲明你的事件階段為 capture 還是 bubble,因為不同的階段會有不同的事件的調用順序,即不同的傳播路徑。
demo中針對默認bubble的調用,所以打印出來的結果為:
SPAN
A
DIV
如果把demo中的參數從false轉換為true,
document.getElementById('sampleSpanId').addEventListener('click',clickHandler,true); document.getElementById('sampleDivId').addEventListener('click',clickHandler,true); document.getElementById('sampleAId').addEventListener('click',clickHandler,true);
則打印出來的結果為:
DIV
A
SPAN
如果將demo中的參數部分div標簽設置為false,a標簽設置為true,
document.getElementById('sampleSpanId').addEventListener('click',clickHandler,true); document.getElementById('sampleDivId').addEventListener('click',clickHandler,false); document.getElementById('sampleAId').addEventListener('click',clickHandler,true);
則打印出來的結果為:
A
SPAN
DIV
二.階段(Phase)在lightning中的使用
官方文檔里面給出了一個例子很好,在這里直接引用過來。
1. 創建一個事件:compEvent
1 <aura:event type="COMPONENT" description="Event template"> 2 </aura:event>
2.創建eventBubblingEmitter.cmp及其對應的controller.js用於注冊事件以及點擊按鈕后觸發事件。
1 <aura:component> 2 <aura:registerEvent name="bubblingEvent" type="c:compEvent" /> 3 <lightning:button onclick="{!c.fireEvent}" label="Start Bubbling"/> 4 </aura:component>
1 ({ 2 fireEvent : function(cmp) { 3 var cmpEvent = cmp.getEvent("bubblingEvent"); 4 cmpEvent.fire(); 5 } 6 })
3.創建eventBubblingGrandChild.cmp,包含了eventBubblingEmitter組件以及添加了事件的handler,一個元素可以通過<aura:handler>標簽執行他自身的事件。
1 <aura:component> 2 <aura:handler name="bubblingEvent" event="c:compEvent" action="{!c.handleBubbling}"/> 3 <div class="grandchild"> 4 <c:eventBubblingEmitter /> 5 </div> 6 </aura:component>
1 ({ 2 handleBubbling : function(component, event) { 3 console.log("Grandchild handler for " + event.getName()); 4 } 5 })
4.創建eventBubblingChild.cmp。此事件緊使用aura:handler聲明了句柄,並未包含任何其他的component
1 <aura:component> 2 <aura:handler name="bubblingEvent" event="c:compEvent" action="{!c.handleBubbling}"/> 3 <div class="child"> 4 {!v.body} 5 </div> 6 </aura:component>
1 ({ 2 handleBubbling : function(component, event) { 3 console.log("Child handler for " + event.getName()); 4 } 5 })
5.創建eventBubblingParent.cmp以及對應的controller。
1 <aura:component> 2 <aura:handler name="bubblingEvent" event="c:compEvent" action="{!c.handleBubbling}"/> 3 <div class="parent"> 4 <c:eventBubblingChild> 5 <c:eventBubblingGrandchild /> 6 </c:eventBubblingChild> 7 </div> 8 </aura:component>
1 ({ 2 handleBubbling : function(component, event) { 3 console.log("Parent handler for " + event.getName()); 4 } 5 })
6. 創建eventBubblingParentApp.app.用於可視化顯示這些組件元素。
1 <aura:application> 2 <c:eventBubblingParent /> 3 </aura:application>
結果展示:
這里可能有兩個疑問:
1.為什么第一個注冊了事件以后,后期的直接使用aura:handler來進行執行事件,而不是每一個都需要注冊事件?
2.為什么輸出的結果是兩項,而不是三項Log?
分析:
1. 當父元素組件在他的標簽里面實例化了子元素的元素組件后,可以直接使用aura:handler來執行事件。
2.我們可以看到eventBubblingParent.cmp中層級結構為 eventBubblingParent > eventBubblingChild > eventBubblingGrandchild. 盡管eventBubblingChild是eventBubblingGrandchild的父級結構,但是lightning component event中,在組件元素中,只有最外層元素組件事件才可以被處理。所以這里面只會執行上述兩個。
我們來將eventBubblingChild.cmp修改一下:
1 <aura:component> 2 <aura:handler name="bubblingEvent" event="c:compEvent" action="{!c.handleBubbling}"/> 3 <!-- <div class="child"> 4 {!v.body} 5 </div> --> 6 <div class="child"> 7 <c:eventBubblingGrandchild /> 8 </div> 9 </aura:component>
此組件元素中, eventBubblingChild 變成了eventBubblingGrandchild的最外層的組件元素,所以輸出的時候回輸出三個log結果。
結果展示:
我們可以看一下這些組件元素構成的傳播順序:
Parent handler -> Child handler -> grandchild -> Child handler -> Parent handler.
針對Bubble方式,從事件源到根為 grandchild -> Child handler -> Parent handler
針對Capture方式,從根到事件源為Parent handler -> Child handler -> grandchild.
上面的例子都是使用Bubble方式的,下面再次修改eventBubblingChild,使他 handler方式修改成capture。區別僅限於添加phase屬性。
1 <aura:component> 2 <aura:handler name="bubblingEvent" event="c:compEvent" action="{!c.handleBubbling}" phase="capture"/> 3 <!-- <div class="child"> 4 {!v.body} 5 </div> --> 6 <div class="child"> 7 <c:eventBubblingGrandchild /> 8 </div> 9 </aura:component>
結果展示:
事件Event對象也包含了很多方法,常用的有以下幾種:
1.event.setParam(obj):此方法用於事件處理時,添加事件的參數,正常事件聲明時,允許有param,此demo中因為便於展示,所以沒有添加param,參看上節;
2.event.fire():此方法用於觸發事件;
3.event.stopPropagation(): 此方法用於停止事件在其他的組件元素傳播;
上面內容中將Grandchild handler 的controller.js修改成以下:
1 ({ 2 handleBubbling : function(component, event) { 3 console.log("Grandchild handler for " + event.getName()); 4 event.stopPropagation(); 5 } 6 })
結果展示:事件執行完 Grandchild handler以后,因為handler中執行了 stopPropagation方法,則后續的handler均不再執行。
4.event.pause():用於暫停正在執行的事件,直到調用event.resume()方法以后才會繼續傳播事件。這種常用於通過異步返回結果來判斷后續要如何執行的場景;
5.event.resume():和 event.pause()一組。
總結:此篇主要講解lightning component event中事件的兩個階段的區別以及用法,兩種用法沒有什么缺點和優點的划分,具體要使用哪種階段需要考慮你的業務場景要怎樣的順序傳播事件。篇中內容有錯誤的地方歡迎指正,有不懂得地方歡迎留言。