上一篇博客我和你們分享了vue render函數的基礎使用html
這篇博客咱們來簡單講一講render函數他是怎麼實現得vue
先來一張官方得圖node
在實例初始化得時候,html經過render函數編譯生成了一個虛擬dom,視圖層會根據虛擬dom生成一個真實domgit
而後若是響應數據發生變化得時候,render函數會從新調用,在更新得時候render函數會返回一個新的虛擬dom , 這個新的虛擬dom並不會直接把以前得替換掉,他會對比新舊dom,而後經過diff算法,把改動最小得結果,拿去更新真實dom,避免發生大量得dom迴流和重繪github
剛纔在上邊已經說過了,render函數會在響應數據發生改變的時候去被觸發,那麼就不得不說一說vue的響應式原理算法
以前就據說過vue實現的原理是數據劫持,那麼他究竟是怎麼作到的數據劫持這件事呢?數組
在vue的源碼裏邊能夠看到,vue實例在初始化的時候會把data裏邊全部的屬性添加一個對象進去babel
function observe (obj) { // 迭代對象的全部屬性 // 並使用Object.defineProperty()轉換成getter/setters Object.keys(obj).forEach(key => { let internalValue = obj[key] // 每一個屬性分配一個Dep實例 const dep = new Dep() Object.defineProperty(obj, key, { // getter負責註冊訂閱者 get () { dep.depend() return internalValue }, // setter負責通知改變 set (newVal) { const changed = internalValue !== newVal internalValue = newVal // 觸發後從新計算 if (changed) { dep.notify() } } }) }) return obj }
在沒有更改屬性的原有行爲的基礎上加了一個依賴對象進去,而後經過這個以來對象來發送通知告訴監聽watcher 數據發生了變化 ,而後watcher去調用render函數 更新domdom
jsx事js內定義的一套類xml語法,能夠解析出js代碼,由js引擎解析,在上一篇博客裏邊我使用的是render函數自己的寫法,可是感受十分的冗餘,jsx裏邊則很是簡介,你能夠直接返回一個標籤的全部屬性,感受和html沒什麼兩樣 好比說函數
render() { const { count, onChange } = this; return ( <div> <componment style={{ marginTop: "10px" }} count={count} type="button" onChange={onChange} /> <componment style={{ marginTop: "10px" }} count={count} type="button" domPropsInnerHTML={`hello ${this.count}.`} onChange={onChange} /> </div> ); }
上邊的代碼能夠輕鬆的渲染出來兩個組件
須要注意的是使用jsx語法的時候須要babel插件,官方插件鏈接
render函數裏邊用的createElement方法建立的vnode,createElement方法在vue的源碼裏邊是對_createElement方法的封裝,以前是由五個參數的,其中有一個參數是對子節點的犯規,
咱們使用的createElement在通過規範化以後,默認返回的一個vnode類型的數組
vnode有四個屬性
第一個是節點的名稱 首先會對tag進行一個判斷,若是是字符串的話,會去看是否是html規範的一些檢點,若是是的話會建立一個普通的vnode節點
若是是以惡搞註冊過的組件名稱,則會createComponent 建立一個組件類型的vnode
若是都不是則會建立一個位置的標籤
所謂虛擬DOM,是一個用於表示真實 DOM 結構和屬性的 JavaScript 對象,這個對象用於對比虛擬 DOM 和當前真實 DOM 的差別化,而後進行局部渲染從而實現性能上的優化。
在render函數生成完畢虛擬dom樹以後 就開始了vnode的首次渲染,vue裏邊調用的是_update方法
// src/core/instance/lifecycle.js Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) { const vm: Component = this if (vm._isMounted) { callHook(vm, 'beforeUpdate') } const prevEl = vm.$el const prevVnode = vm._vnode const prevActiveInstance = activeInstance activeInstance = vm vm._vnode = vnode if (!prevVnode) { // 初始化渲染 vm.$el = vm.__patch__( vm.$el, vnode, hydrating, false /* removeOnly */, vm.$options._parentElm, vm.$options._refElm ) // no need for the ref nodes after initial patch // this prevents keeping a detached DOM tree in memory (#5851) vm.$options._parentElm = vm.$options._refElm = null } else { // 更新渲染 vm.$el = vm.__patch__(prevVnode, vnode) } activeInstance = prevActiveInstance // update __vue__ reference if (prevEl) { prevEl.__vue__ = null } if (vm.$el) { vm.$el.__vue__ = vm } // if parent is an HOC, update its $el as well if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) { vm.$parent.$el = vm.$el } // updated hook is called by the scheduler to ensure that children are // updated in a parent's updated hook. }
能夠看出無論如何最終都是調用的__patch__方法
這個__patch__方法主要有兩個做用 第一個是建立真實dom 另一個是發生變化以後把新老的虛擬dom進行對比而後更新真實dom
這個方法對應的方法是createPatchFunction() 有興趣的能夠去源碼裏邊搜一下
以上是我對render函數的從響應到渲染還有他的做用的理解 有不對的地方歡迎批評指正