[Vue原始碼分析] v-model實現原理
最近小組有個關於vue原始碼分析的分享會,提前準備一下…
前言:
我們都知道使用v-model可以實現資料的雙向繫結,及實現資料的變化驅動dom的更新,dom的更新影響資料的變化。那麼v-model是怎麼實現這一原理的呢?接下來探索一下這部分的原始碼。
前期準備
①:vue2.5.2原始碼(用於閱讀、檢視關聯等)
②:建立vue demo,建立包含v-model指令的例項(用於debugger)
以下為demo:
genDirectives
在模板的編譯階段, v-model跟其他指令一樣,會被解析到 el.directives 中,之後會通過genDirectives方法處理這些指令,genDirectives方法位於src/compiler/codegen/index.js中:
我們去到之前建立的demo,找到demo中node_modules/vue/dist/vue.esm.js下的genDirectives方法,打上debugger,如圖:
可以看到傳進來的el是ast語法樹,el.directives是el上的指令,如下:
回到
genDirectives
原始碼,迴圈指令時都執行了const gen: DirectiveFunction = state.directives[dir.name]
這個方法,state.directives
是什麼?當遍歷到
v-model
的時候,dir.name
為model
,對應的state.directives[dir.name]
state.directives[model]
,directives
的定義位於src/platforms/web/compiler/directives/index.js
中:本次分析的
v-model
,對應的也就是model
方法,也就是其實!!gen(el, dir, state.warn)
執行的是model
方法,!!
用於將返回值轉為Boolean
型別,model
的定義位於index
同目錄下。
model
model
方法根據傳入的引數對tag
的型別進行判斷,呼叫不同的處理邏輯,本demo中tag
的型別為input
,所以會執行genDefaultModel
方法,為了節約時間,就不去原始碼中找了,藏得比較深,直接在demo引用的單檔案原始碼vue.esm.js
genDefaultModel
。
genDefaultModel
發現定義如下,打上debugger,以便除錯:
通過控制檯檢視變數資訊,可以看到:
可以看到裡邊的genAssignmentCode(value, valueExpression)
在此demo中相當於genAssignmentCode("msg", ""$event.target.value"")
,執行此方法後返回的是一個字串:msg=$event.target.value
,後來命中了needCompositionGuard
,所以code
變成了if($event.target.composing)return;msg=$event.target.value
,if($event.target.composing)return;
的作用是不記錄使用者未確定的輸入,比如:
註釋掉if(needCompositionGuard)
的話使用者沒確定的也會展示,如圖:
隨後會依次執行以下兩個方法:
addProp
先註釋掉addHandler
,避免對研究此方法產生影響。
可以看到此方法的功能為給el
新增props
,首先判斷el
上有沒有props
,如果沒有的話建立props
並賦值為一個空陣列,隨後拼接物件並推到props
中,程式碼在此demo中相當於push
了{name: "value", value: "(msg)"}
,列印一下這番操作後的el,可以看到添加了props
的el
的結構如下:
這個方法其實是在inpu
t上動態綁定了value
,此時,原本的<input v-model="msg">
相當於變成了<input v-bind:value="msg">
,隨後繼續執行addHandler
。
addHandler
以下僅包含關鍵程式碼,打上debugger
。
控制檯檢視el
的debuuger
結果:
可以看到比執行addHandler
之前,el
上多了events
,可以得知這個方法主要給el
添加了事件處理,在此demo中的話相當於在 input 上綁定了 input 事件。
總結:
也就是說,到此為止,原本的<input v-model="msg">
相當於變成了<input v-bind:value="msg" v-on:input="msg=$event.target.value">
,當用戶輸入的使用觸發msg=$event.target.value
進而更新msg
,msg
通過v-bind
繫結到輸入框的value上。
即,以下兩份程式碼其實是一個意思。
第一份:
第二份