寫文章不容易,點個讚唄兄弟
專一 Vue 源碼分享,文章分爲白話版和 源碼版,白話版助於理解工做原理,源碼版助於瞭解內部詳情,讓咱們一塊兒學習吧
研究基於 Vue版本 【2.5.17】
若是你以爲排版難看,請點擊 下面連接 或者 拉到 下面關注公衆號也能夠吧dom
說到 Vue,感受第一印象就是雙向綁定,因此v-model鍵值是Vue印象的半壁江山啊,這麼重要的東西,你好歹要知道是怎麼實現的吧學習
咱們今天就來說解雙向綁定的工做原理,你應該知道,雙向綁定就是經過綁定 dom 事件來實現的,但是,怎麼綁定的事件,綁定什麼事件?this
首先,雙向綁定,我我的認爲應該分爲 初始化綁定 和 雙向更新 兩part。spa
初始化綁定,就是初始化時給表單元素綁定值,綁定事件,爲雙向更新作準備3d
雙向更新,就是任意一邊變化,同時能讓另外一個邊更新雙向綁定
雙向更新那是後話,只有一開始時成功執行綁定操做纔會有以後 雙向更新這個東西,因此,今天按順序來了解兩個part,從四個問題開始code
一、v-model 怎麼給表單綁定數據對象
二、v-model 綁定什麼事件token
三、v-model 怎麼綁定事件
四、v-model 如何進行雙向更新
TIP
v-model 還能夠用在 自定義組件上,可是很明顯,此次咱們先不講這一塊,而是先將正常的表單使用
咱們先以 input text 類型講解,對於其餘的表單元素,流程都差很少,只是中間涉及的內容不一樣。因此就先講個例子,而後具體在源碼版所有一塊兒說
一、怎麼賦值?v-model 綁定的數據賦值給表單元素的 value 屬性
二、怎麼綁定事件?解析不一樣表單元素,配置相應的事件名和事件回調,在插入dom以前,addEventListener 綁定上事件
三、怎麼雙綁?外部變化,觸發事件回調,event.target.value 賦值給model綁定的數據;內部變化,修改表單元素屬性 value
看完結論,有點懵?咱們來看看具體的內容,結果導向來進行學習
下面的講解如下面這個爲例
獲取值流程
首先,上面例子解析後的渲染函數是下面這樣(已簡化,只保留表單值相關)
(function(){ with(this){ return _c('div',[ _c('input', domProps:{"value":name} ) ]) } })
一、這個渲染函數是沒有執行的 匿名函數。執行的時候,會綁定上下文對象爲 組件實例
二、因而 with(this) 中的 this 就能取到 組件實例自己,with 的代碼塊 頂層做用域 就綁定爲了 組件實例
三、因而 with 內部變量的訪問,就會首先訪問到 組件實例上。其中 name 的 獲取,就會先從 組件實例上獲取,至關於 vm.name。賦值完成後,domProps 就是下面這樣
{ domProps:{value:111} }
四、上面的 value 是 v-model 解析成的原生屬性,保存在屬於該節點 input 的 domProps 對象存儲器中
綁定值流程
建立dom input 以後,插入dom input 以前,遍歷該 input 的 domProps ,逐個添加給 input dom
簡化後的代碼像下面這樣進行賦值
for(var i in domProps){ input[i]=domProps[i] }
其中給節點賦值就是這樣
input.value = 111
不一樣的表單元素使用v-model,會綁定不一樣的 事件
change 事件
select,checkbox,radio
input 事件
這是默認事件,當不是上面三種表單元素時,會解析成 input 事件
好比 text、number 等 input 元素和 textarea
TIP
實際上關於 input 和 textarea 綁定的事件遠不止 input 一個事件,可是涉及內容太多,打算放在源碼版說明,這裏先簡單默認是 input 事件
上面例子解析成下面的渲染函數(已簡化,只保留事件相關)
with(this) { return _c('div', [ _c('input', { on: { "input": function($event) { name = $event.target.value } } } )]) }
解析事件流程
1解析不一樣表單元素,配置不一樣的事件
2拼裝 事件回調函數,不一樣表單元素,回調不同
3把 事件名和拼裝回調 配套 保存給相應的表單元素的 on 事件存儲器
何時綁定事件
生成 input dom 以後,插入input dom 以前
怎麼綁
使用以前保存的 事件名和 事件回調,給 input dom 像下面這樣綁定上事件
input.addEventListener("input",function($event) { name = $event.target.value })
雙向,指的是 外部和內部
外部變化:用戶手動改變表單值,輸入或者選擇
內部變化:從內部修改綁定值
內部變化
一、v-model 綁定了 name ,name 會收集到 本組件的 watcher
a. 下面渲染函數執行時,會綁定自己組件實例爲上下文對象
b. name 訪問的是 組件實例的 name
c. name 此時便收集到了 當前正在渲染的組件實例,當前渲染的實例是本身,因而收集到了自身的 watcher
(function(){ with(this){ return _c('div',[_c('input', domProps:{"value":(name)}) ]) } })
若是你不懂 收集 watcher 什麼意思,建議看下個人另外一篇文章
【Vue原理】響應式原理 - 白話版
二、內部修改 name 變化,通知收集器內的 watcher 更新,因此本組件會更新,上面的渲染函數從新執行,便 從新從實例讀取 name
三、從新給 input dom 的 value 賦值,因而 頁面就更新了
怎麼賦值?那是回到第一個問題了,兄弟
外部變化
手動改變表單,事件觸發,執行事件回調(下面這個),更新組件數據 name
function($event) { name = $event.target.value }
更新內部數據流程
一、當事件觸發的時候,會把 表單的值 賦值給 name
二、name 是從 組件實例上訪問的
三、因此此次賦值會 直接改變組件實例的 name
回調怎麼賦值給組件實例的name
一開始不懂,因此不理解,也沒查到,寫了個例子,大概理解了意思
一、由於事件回調 在 with 裏面聲明
二、因而事件回調的 做用域鏈最頂層 就加上了一層 with 綁定的做用域
三、就算事件回調不在 with 中執行,事件回調中的 變量訪問,也會先訪問以前 with 綁定過的做用域,由於做用域鏈的最頂層
with舉栗子
var name=22 var a={name:"a"} with(a){ var fn=function(){ console.log(name) } } fn()
你認爲 fn 執行的時候,會打印出什麼呢?行了,給你看結果了
好吧,再一次深入認識到一個知識點,with 綁定做用域原來這麼強,離開with執行,仍是先訪問他的做用域,脫離不出魔爪啊,強盜做用域
回來總結!
因而當事件回調執行的時候,會 直接賦值 給 組件實例的name,這樣便經過外部改變了內部數據
TIP
外部變化,原本可能會存在一種狀況
a、手動修改表單後, 回調內會更新組件的值
b、組件的值更新以後,會通知組件更新,組件更新時,便又會從新把input 賦值一遍
很是多餘的一步操做,因此這裏,Vue作一個判斷,判斷舊值和 新值是否相等,不等才更新,關於舊值,會保存在 dom 的 _value 屬性
v-model 三要素
一、綁定屬性
二、綁定事件
三、屬性+事件組合完成雙向更新