寫文章不容易,點個讚唄兄弟 <br> <br> 專一 Vue 源碼分享,文章分爲白話版和 源碼版,白話版助於理解工做原理,源碼版助於瞭解內部詳情,讓咱們一塊兒學習吧 研究基於 Vue版本 【2.5.17】node
若是你以爲排版難看,請點擊 下面連接 或者 拉到 下面關注公衆號也能夠吧函數
【Vue原理】Diff - 源碼版 之 重新建實例到開始diff 學習
Diff 的內容不少,咱們先來探索一下從 新建實例 到 開始Diff 的流程走一遍,本文很短this
先對整個流程有個把握,再仔細去探索 Diff 的思想spa
首先,當你新建實例的時候,好比這樣prototype
你調用一個 Vue 函數,因此來看下 Vue 函數code
function Vue() { ... 已省略其餘 new Watcher(function() { vm._update(vm._render()); }) ... 已省略其餘 }
函數中作了兩件事blog
一、爲實例新建一個 watchertoken
二、爲 watcher 綁定更新回調(就是 new Watcher 傳入的 function )ci
每一個實例都會有一個專屬的 watcher,而綁定的回調,在頁面更新時會調用
咱們如今來看下簡化的 Watcher 的源碼
funciton Watcher(expOrFn){ this.getter = expOrFn; this.get(); } Watcher.prototype.get = function () { this.getter() }
watcher 會保存更新回調,而且在新建 watcher 的時候就會馬上調用一遍更新回調
如今咱們繼續看 更新回調的內容
vm._update(vm._render());
若是你看到以前的文章應該知道這兩個函數的做用
如今就來簡單說一下
生成頁面模板對應的 Vnode 樹,好比
生成的 Vnode 樹是( 其中num的值是111 )
{ tag: "div", children:[{ tag: "span" },{ tag: undefined, text: "111" }] }
這一步是經過 compile 生成的,具體的話能夠簡單看下 Compile - 白話版
有興趣的有耐心的也能夠看源碼版
比較 舊Vnode 樹 和 vm._render 生成的新 Vnode 樹 進行比較
比較完後,更新頁面的DOM,從而完成更新
ok,咱們看下源碼
Vue.prototype._update = function(vnode) { var vm = this; var prevEl = vm.$el; var prevVnode = vm._vnode; vm._vnode = vnode; // 不存在舊節點 if (!prevVnode) { vm.$el = vm.__patch__( vm.$el, vnode, vm.$options._parentElm, vm.$options._refElm ); } else { vm.$el = vm.__patch__( prevVnode, vnode ); } };
解釋其中幾個點
看過 VNode - 源碼版 應該知道,這個屬性保存的就是當前 Vnode 樹
當頁面開始更新,而生成了新的 Vnode 樹以後
這個屬性則會替換成新的Vnode
因此保存在這裏,是爲了方便拿到 舊 Vnode 樹
是的,沒有錯,你在兩處地方看到這個東西
這個東西就是 Diff 的主要內容,內有乾坤,內容不少,不會在這裏說,畢竟今天只探索流程
可是要看看這個東西怎麼來的
var patch = createPatchFunction(); Vue.prototype.__patch__ = patch ;
嗯,是通過一個 createPatchFunciton 生成的
而後賦值到 Vue 的原型上,因此能夠 vm.patch 調用嘍
咱們再來講說 _update 函數中出現的那兩處 patch
不須要進行比較,直接所有建立
vm.$el 保存的是 DOM 節點,若是不存在舊節點,那麼 vm.$el 此時也是不存在的
而傳入 vm.$el 爲空的時候,patch 拿到這個值判斷爲空的時候,就直接建立DOM,不會去作其餘操做了
須要把舊節點和新節點比較,儘可能找到最小差別部分,而後進行更新,這部份內容就是 Diff 的重點了,須要花費很多精力的。會放在其餘文章進行記錄
鑑於本人能力有限,不免會有疏漏錯誤的地方,請你們多多包涵,若是有任何描述不當的地方,歡迎後臺聯繫本人,有重謝