vue中 点击事件的写法_vue中的事件:原生事件与自定义事件__Vue.js
模板編譯 processAttrs
對于ast attributes處理(v-on/@)
利用onRE與dirRE來捕獲事件
這里最重要的就是dynamic的判斷,vue中可以用動態參數來命名事件名稱,如@[prop],prop為data中的值。不過通常都是一個靜態的事件名稱如 @click
另一個核心方法就是addHandler
addHandler 往AST上添加events屬性
1. 這里會對動態事件名稱進行一些處理,也會對right,middle修飾符處理,另外對capture, passive, once的事件名稱做了標記。分別添加 !, ~, # 符號。然后刪除了對應屬性值
2. 參數value為@click="handler"例子中的handler,在此處還在編譯階段handler本質上是一個string。然后對value做了處理,記錄了模板解析event時候start和end的位置。
3. 對于單個事件有多回調函數綁定的情況,添加了一個important參數,以此來提前觸發當前回調函數的執行
4. 另外,這里對鼠標的right和middle做了處理,在處理只有刪除了對應屬性值。對于鍵盤的事件沒有做處理
AST -> code
修飾符:modifierCode
上面已經提到過了,本質上模板編譯的時候會利用正則處理各種修飾符,然后根據對應關鍵詞的生成代碼。如常用的stop,prevent。
核心方法就是src/compiler/codegen/events.js的genHandler方法,以下是分析:
1. genHandlers
該方法就是簡單的遍歷events對象的鍵值對然后,對有無native修飾符與是否為dynamic事件做一個處理,其核心方法就是調用genHandler
genHandler
一些參數判斷
事件函數的多種寫法
在官方文檔中演示了事件回調函數的多種寫法,這些寫法都在模板編譯過程中進行了識別
下方三個正則表達式是模板編譯時對event寫法的判斷依據,下面會有更詳細的注釋
1. 路徑類寫法
2. 箭頭函數,匿名函數
3. 表達式 handler($event), a = 1
在vue中通常通過@click="handle($event)"來獲取event對象,其實這里是vue在模板中做了一層包裹,將function($event){}套在handle($event)外部。
有修飾符的情況
修飾符在ast生成的過程中就已經捕獲了,vue中對event事件的修飾符處理如下
先對修飾符做一個處理
最后座一層包裹,因為這里對鍵盤事件也做了處理,因此一定要拿到event對象
genCode
在處理完這些之后會生成字符串on:"…"/nativeOn:"…",最后生成render函數
組件初始化
組件初始化簡單地說就是先對options做各種處理,最后執行渲染watcher,生成頁面。
對native events的處理:
platforms/web/runtime/patch.js中有
const patch = createPatchFunction({nodeOps, modules})
modules源自:web/runtime/modules/index,導出含生命周期的對象**(非created周期)**
createPatchFunction
createPatchFunction往hooks內添加了updateDOMListeners,hooks為**并非是**組件的生命周期函數。在組件create與updated的時候就會觸發updateDOMListeners函數。注冊事件。
注意點: 這里create并非是組件created的**周期函數。**是在真實節點創建之后才會觸發鉤子,因此是可以拿到真實節點的。
updateDOMListeners
在調用modules.create的hook的時候觸發了updateDOMListeners
其作用就是給用addEventListeners與removeEventListeners方法給真實dom節點添加事件。
updateListeners
將新舊事件進行對比更新。這里的add和remove可以給真實dom注冊事件,也可以給組價注冊事件。
占位符$vnode的真實dom事件:createComponent(部分)
對于組件而言,data.on賦值給listeners,把data.nativeOn賦值給data.on
data.on里面存放著的都是原生dom事件,組件內部的listeners都是用戶自定義的事件。
因此,在組件patch過程中,創建組件的根節點的時候,就會把data.on內部的原生dom事件注冊在dom上。
因此如果在h5元素使用native如 ,vue就會報錯。這正是因為在這里做了處理,只有占位符vnode才可以有data.nativeOn的屬性。是h5標簽的節點不會調用createComponent方法,其data.on在創建節點的時候會綁定到節點上。
自定義事件(只針對組件間)
由createComponent函數可知,listeners存放了自定義事件。
在父子通訊的時候父組件只要v-on/@eventName,就可以監聽到子組件emit出來的事件。
_init: initInternalComponent
創建子組件的時候會把占位符$vnode的$listeners傳遞給子組件的$options**(此時data.on已經是data.nativeOn,原始的data.on賦值給變量listeners)**
_init: initEvents
調用updateComponentListeners方法,最后還是調用了updateListeners方法(見前面文章注釋)。
但是這里不同的是add和remove方法并非是document.addEventListener和document.removeEventListener
add/remove(vue中的發布訂閱模式):
其實發布訂閱模式比較簡單,就不詳細說明了,主要是add/remove方法。
小結
其實本質上,還是將父組件注冊的回調函數傳給了子組件的_events對象(讓該函數存在于子組件中),但是看起來像是子組件調用了父組件的方法
eventBus
對于跨組件的組件通信,利用了vue實例可以有自身的_events對象,因此在Vue原型上創建一個空的vue實例,然后將vue所有組件上的函數都注冊在這個實例對象的_events對象上,一次達到跨組件通信的目的
Vue.prototype.$bus=new Vue()
第三種情況:v-on="$listeners"
$listeners就是vnode.data.on的別稱,因此通過$listeners就可以拿到父組件注冊的非native事件。在爺孫組件通信的是可以使用$listeners通過父級組件將爺孫組件關聯。
grand組件:
father組件:? // 此處是grand的事件
son組件:this.$emit(“test”) // 將father的$listeners傳入son,而father的$listeners包含grand的事件,因此就將grand的事件傳入了son中。
AST解析
在模板解析的時候會用正則匹配v-on,對于v-on="$listeners",vue將這種寫法視為指令(directive),有不同的策略
on指令
_g
這里將$listeners對象與data.on進行合并,通過v-on指令我們可以一次性對組件注冊多個事件。
最后
vue的事件基本上就是這樣,其實這里面牽扯到了最核心的組件初始化和更新流程,關于此部分在本文中并沒有明確說明,只是大概提了一下,一是要單純靠文字說明費時費力(代碼含有大量遞歸),而是本文重點是關注vue中的事件,在后面的更深入了解整個機制后會嘗試說明寫一篇vue組件初始化及更新的文章。
版權聲明:著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
作者: Simplyme0823
原文鏈接:https://juejin.im/post/6861206075744452622
總結
以上是生活随笔為你收集整理的vue中 点击事件的写法_vue中的事件:原生事件与自定义事件__Vue.js的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: hbuild json红叉_MUI+Hb
- 下一篇: vue跳转到外部链接_前端实战项目:Vu