新聞中心
說起事件,做前端開發(fā)的朋友一定不會(huì)陌生。事件,即網(wǎng)頁上的一系列行為,可以是瀏覽器行為,如頁面完成了加載,頁面關(guān)閉;或是用戶操作行為,如用戶輸入操作,用戶點(diǎn)擊按鈕等,這些行為會(huì)被JavaScript監(jiān)測到,并執(zhí)行相應(yīng)的邏輯代碼??梢哉f,前端的交互行為與事件機(jī)制息息相關(guān),對于前端開發(fā)者而言,掌握好事件機(jī)制是絕對必要的。

所謂組件,即封裝起來的具有獨(dú)立功能的UI部件。試想,如果開發(fā)一個(gè)復(fù)雜的頁面,開發(fā)者把所有的UI部分寫在一個(gè)文件中,這樣的代碼顯然可維護(hù)性很低。但我們?nèi)绻媒M件的方式去重新思考UI構(gòu)成,將UI上每一個(gè)功能相對獨(dú)立的模塊定義成組件,然后將小的組件通過組合或者嵌套的方式構(gòu)成大的組件,進(jìn)而完成整體UI的開發(fā)。這樣,我們不僅提高了代碼的復(fù)用性,且整體結(jié)構(gòu)清晰,維護(hù)性則大大提高。
組件化界面
本文將介紹在快應(yīng)用開發(fā)中,事件相關(guān)的主要API以及事件的監(jiān)聽、觸發(fā)機(jī)制,同時(shí)會(huì)介紹快應(yīng)用中組件是如何通信的。閱讀本文前,建議先了解快應(yīng)用相關(guān)基礎(chǔ)知識。
自定義事件的監(jiān)聽、移除與觸發(fā)
$on 用于監(jiān)聽自定義事件;$off移除對應(yīng)的事件監(jiān)聽;$emit()、$dispatch()、 $broadcast()等方法可用于觸發(fā)事件。
$on(evtName, fnHandler)事件
在當(dāng)前頁面注冊監(jiān)聽事件, 可監(jiān)聽$emit()、 $dispatch()、 $broadcast()等觸發(fā)的自定義事件,不能用于注冊組件節(jié)點(diǎn)的事件響應(yīng)。
示例如下:
- export default {
- onInit(){
- this.$on('customEvtType1', this.customEvtType1Handler)
- },
- customEvtType1Handler(evt){
- // 事件類型,事件參數(shù)
- console.info(`觸發(fā)事件:類型:${evt.type}, 參數(shù): ${JSON.stringify(evt.detail)}`);
- }
- }
解釋一下
'customEvtType1'為該組件上自定義的事件名稱,customEvtType1Handler為當(dāng)'customEvtType1'事件被觸發(fā)時(shí),要執(zhí)行的回調(diào)函數(shù)。
$off(evtName, fnHandler)
移除事件監(jiān)聽,參數(shù) fnHandler 為可選,傳遞僅移除指定的響應(yīng)函數(shù),不傳遞則移除此事件的所有監(jiān)聽。
示例如下:
- export default {
- removeEventHandler () {
- // 不傳遞fnHandler:移除所有監(jiān)聽
- this.$off('customEvtType1')
- // 傳遞fnHandler:移除指定的監(jiān)聽函數(shù)
- this.$off('customEvtType1', this.customEvtType1Handler)
- }
- }
頁面的交互中可能會(huì)遇到一些非手動(dòng)觸發(fā)的需求,$emit() 通過觸發(fā)當(dāng)前實(shí)例上的事件,達(dá)到動(dòng)態(tài)觸發(fā)事件的行為,類似jquery中的trigger方法。
$emit(evtName, evtDetail)
觸發(fā)當(dāng)前實(shí)例監(jiān)聽事件函數(shù),與 $on() 配合使用,注意:$emit() 目前只觸發(fā) $on 所監(jiān)聽的事件
示例如下:
- export default {
- emitEvent () {
- this.$emit('customEvtType1', { params: '參數(shù)內(nèi)容' })
- }
- }
監(jiān)聽原生組件事件
原生組件即框架自帶的組件,如div,text等等,其支持一系列事件,如通用事件(如:click, disappear)、組件專有事件(如:focus)。完整的原生組件列表以及事件可在快應(yīng)用官網(wǎng)查詢到。
開發(fā)者可以在事件回調(diào)函數(shù)中,獲取到當(dāng)前觸發(fā)組件的信息,并進(jìn)行進(jìn)一步的操作。
在響應(yīng)函數(shù)執(zhí)行時(shí)通過target獲取,如:onClickHandler
在響應(yīng)函數(shù)綁定時(shí)傳遞參數(shù),如:onClickHandler2
示例如下:
組件節(jié)點(diǎn)1 組件節(jié)點(diǎn)2
解釋一下
onClickHandler函數(shù)如果不傳參數(shù),默認(rèn)參數(shù)env即為當(dāng)前觸發(fā)組件的實(shí)例;若傳遞了參數(shù),如onClickHandler2,則參數(shù)安順序排列,env為***一個(gè)參數(shù)。
觸發(fā)原生組件事件
用戶可通過手動(dòng)操作觸發(fā)事件,如點(diǎn)擊事件等,除此之外,也可以在代碼中通過$emitElement()完成事件的動(dòng)態(tài)觸發(fā),類似上文自定義組件中的$emit()方法。
$emitElement(evtName, evtDetail, id)
可以觸發(fā)指定組件id的事件,通過evt.detail獲取傳遞的參數(shù);該方法僅用于原生組件,對自定義組件無效。
示例如下:
觸發(fā)組件節(jié)點(diǎn)的事件:click 組件節(jié)點(diǎn)1 組件節(jié)點(diǎn)2
解釋一下
Click事件可通過用戶點(diǎn)擊操作觸發(fā),也可通過$emitElement觸發(fā)。
自定義組件
上文曾提到原生組件,通常原生組件是我們系統(tǒng)中最基礎(chǔ)的組件,然而我們在做一個(gè)稍微復(fù)雜的頁面時(shí),如果每個(gè)頁面都只用原生組件搭建,那這樣的代碼的可維護(hù)性會(huì)差很多。打個(gè)比方,就好比一個(gè)人口眾多的國家,沒有省、市、縣這些單位,而是只以個(gè)人為單位,難以想象這個(gè)國家的管理將有多難。道理類似,自定義組件,我們可以根據(jù)具體的業(yè)務(wù)邏輯,把頁面按照功能拆成多個(gè)模塊,每個(gè)模塊負(fù)責(zé)其中的一個(gè)功能部分,***頁面將由這些模塊組合搭建起來,讓代碼結(jié)構(gòu)更加清晰,易于維護(hù)。
自定義組件是開發(fā)者編寫的組件,使用起來和Native原生組件一樣,最終按照組件的來渲染;同時(shí)開發(fā)起來又和頁面一樣,擁有ViewModel實(shí)現(xiàn)對數(shù)據(jù)、事件、方法的管理,這么來看,頁面也是一種特殊的自定義組件,無需引入即可使用,同時(shí)服務(wù)于整個(gè)頁面。
編寫自定義組件
示例如下:
自定義組件: {{ prop1 }} {{ prop2Object.name }}
兩點(diǎn)注意
一是自定義組件比頁面組件的不同之處在于多了一個(gè)props屬性,用于聲明該組件可接受的外部數(shù)據(jù)傳遞;props是一個(gè)數(shù)組,數(shù)組中每個(gè)元素是暴露的屬性。
二是如果屬性名稱使用駝峰定義,如:prop2Object,那么在外部傳遞數(shù)據(jù)時(shí)請使用-連接,如:prop2-object
引入自定義組件
引入自定義組件的方式和我們平時(shí)常用的方式不同,我們平時(shí)通常會(huì)用require或import的方式引入組件,而在快應(yīng)用框架中,需要使用
示例如下:
最終頁面定義與引入方式如下:
頁面組件: {{ data1 }} {{ data2.name }} 觸發(fā)$broadcast()
解釋一下
上面的代碼中有幾點(diǎn)需要說明:
1 在comp-part1標(biāo)簽中,我們看到這樣一個(gè)屬性,onevt-type3="evtTypeHandler",這是指,在該節(jié)點(diǎn)上綁定了名為evtType3的方法,被觸發(fā)后,執(zhí)行evtTypeHandler的函數(shù),在下文的‘父子之間組件事件傳遞’中,會(huì)看到如何觸發(fā)該方法。
2 代碼中的evtType1Emit方法,該方法通過調(diào)用$broadcast方法,觸發(fā)了名為'evtType1'的事件,并傳遞了params參數(shù),'evtType1'事件也可以在下文‘父子組件之間事件傳遞’中看到。
傳遞數(shù)據(jù)與數(shù)據(jù)改造
如上面所述,父組件向子組件傳遞數(shù)據(jù),通過在子組件的props屬性中聲明對外暴露的屬性名稱,然后在組件引用標(biāo)簽上聲明傳遞的父組件數(shù)據(jù)。
如果你需要在子組件中對數(shù)據(jù)進(jìn)行改造,但又不想改動(dòng)父組件數(shù)據(jù)時(shí),可以使用$watch()來滿足需求。如果是監(jiān)聽對象中的屬性,參數(shù)請使用.分割,如:$watch(xxx.xxx.xxx, methodName)
示例如下:
解釋一下
上面是子組件的代碼,我們看到data中定義了upperProp1,同時(shí)也看到watchPropsChange方法中,有兩個(gè)參數(shù),一個(gè)是newV,指變化后的屬性值,oldV指原先的屬性值,將newV賦值給upperProp1,這樣在子組件中對數(shù)據(jù)upperProp1進(jìn)行改造,就不會(huì)改動(dòng)父組件原先的數(shù)據(jù)。
父子組件之間的事件傳遞
當(dāng)子組件對數(shù)據(jù)進(jìn)行改造后,把最終數(shù)據(jù)交給父組件甚至往上,往往有兩種辦法
1、父組件傳遞的數(shù)據(jù)本身就是對象,子組件直接修改的就是這個(gè)對象中的屬性;那么父組件同樣也就拿到了最終數(shù)據(jù)
2、子組件在data中保存了一份內(nèi)部數(shù)據(jù),需要交給父組件:子組件通過$dispatch()完成事件觸發(fā),父組件通過$on()綁定事件并響應(yīng),如:evtType2;
類似于2,子組件在data中保存了一份內(nèi)部數(shù)據(jù),需要交給父組件:子組件通過$emit()觸發(fā)在節(jié)點(diǎn)上綁定的事件來執(zhí)行父組件的方法,如:evtType3;
示例如下:
解釋一下
在上文我已做了如下說明
1 在父組件的comp-part1標(biāo)簽中,我們看到這樣一個(gè)屬性,onevt-type3 = "evtTypeHandler",這是指,在該節(jié)點(diǎn)上綁定了名為evtType3的方法,如果子組件中evtType3Emit調(diào)用執(zhí)行,則會(huì)執(zhí)行父組件中的evtTypeHandler的函數(shù), 從而完成子組件與父組件的通信。
2 父組件中的evtType1Emit方法,該方法通過調(diào)用$broadcast方法,觸發(fā)了名為'evtType1'的事件,并傳遞了params參數(shù),'evtType1'事件則注冊在子組件的onInit方法中,從而完成父組件與子組件的通信。
所以,框架向開發(fā)者提供了雙向的事件傳遞。
向下傳遞:父組件觸發(fā),子組件響應(yīng);調(diào)用parentVm.$broadcast()完成向下傳遞,如:evtType1
向上傳遞:子組件觸發(fā),父組件響應(yīng);調(diào)用childVm.$dispath()完成向上傳遞,如:evtType2
兄弟組件之間的通信
傳統(tǒng)的兄弟等非父子組件之間通信,是通過觀察者模型來完成。觀察者模式的作用是當(dāng)一個(gè)對象的狀態(tài)發(fā)生變化時(shí),能夠自動(dòng)通知其他關(guān)聯(lián)對象,自動(dòng)刷新對象狀態(tài)。
開發(fā)者可以自己寫一個(gè)Pub/Sub模型實(shí)現(xiàn)通信解耦;不過本文并不詳細(xì)介紹如何通過觀察者模式來實(shí)現(xiàn)組件間通信,這個(gè)題目夠另寫一篇文章了。
其實(shí),在業(yè)務(wù)邏輯相對簡單的情況下,我們可以使用ViewModel本身的事件綁定來處理。兄弟組件的相同點(diǎn)是,他們擁有相同的父組件,所以,父組件將是兄弟組件通信的橋梁,可以在以下代碼中看到這個(gè)過程。
示例如下:
子組件定義了Sub端的邏輯處理,有processMessage()、customEventInVm2(),后者同使用$on效果一致
自定義組件2: 處理消息:{{msg}} 事件內(nèi)容:{{eventDetail}}
另外一個(gè)兄弟組件可以通過父組件中建立相互引用達(dá)到相互持有ViewModel的目的,通過在生命周期onReady()中執(zhí)行establishRef()實(shí)現(xiàn),如下代碼所示:
那么另外一個(gè)子組件的Pub端定義就很簡單了,執(zhí)行sendMesssage()即可完成觸發(fā),如下代碼所示:
自定義組件3: 點(diǎn)擊發(fā)送消息
解釋一下
通過上面的例子,我們可以看到,comp-part2和 comp-part3在父組件中通過nextVm和previousVm建立了‘兄弟關(guān)系’,基于此,它們之間可以直接調(diào)用對方的方法(如processMessage),或會(huì)通過$emit方法觸發(fā)對方監(jiān)聽的事件'customEventInVm2'。
結(jié)尾
本文對快應(yīng)用開發(fā)中的事件監(jiān)聽以及觸發(fā)方式做了介紹,通過學(xué)習(xí)我們能夠更好的分離業(yè)務(wù)邏輯,減少方法響應(yīng)上的耦合。另外,通過掌握自定義組件的開發(fā),會(huì)讓我們的項(xiàng)目結(jié)構(gòu)更加明朗,更易于維護(hù);同時(shí)了解父子、兄弟組件之間的數(shù)據(jù)通信,更是完成好快應(yīng)用開發(fā)的必要條件。當(dāng)然,本文多數(shù)素材來源于官方文檔中的示例,但對其中模糊之處做了相應(yīng)解釋和補(bǔ)充,希望能夠給讀者些許幫助。如有不足之處,歡迎指正。
網(wǎng)頁題目:快應(yīng)用的事件監(jiān)聽機(jī)制和組件間通信
文章鏈接:http://fisionsoft.com.cn/article/cdeossd.html


咨詢
建站咨詢
