vue render函數進階學習

上一篇博客我和你們分享了vue render函數的基礎使用html

這篇博客咱們來簡單講一講render函數他是怎麼實現得vue

先來一張官方得圖node

在實例初始化得時候,html經過render函數編譯生成了一個虛擬dom,視圖層會根據虛擬dom生成一個真實domgit

而後若是響應數據發生變化得時候,render函數會從新調用,在更新得時候render函數會返回一個新的虛擬dom , 這個新的虛擬dom並不會直接把以前得替換掉,他會對比新舊dom,而後經過diff算法,把改動最小得結果,拿去更新真實dom,避免發生大量得dom迴流和重繪github

render函數觸發的時機

剛纔在上邊已經說過了,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

render函數中的jsx語法

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插件,官方插件鏈接

createElement

render函數裏邊用的createElement方法建立的vnode,createElement方法在vue的源碼裏邊是對_createElement方法的封裝,以前是由五個參數的,其中有一個參數是對子節點的犯規,
咱們使用的createElement在通過規範化以後,默認返回的一個vnode類型的數組

vnode有四個屬性

  • tag
  • data
  • context
  • children

第一個是節點的名稱 首先會對tag進行一個判斷,若是是字符串的話,會去看是否是html規範的一些檢點,若是是的話會建立一個普通的vnode節點

若是是以惡搞註冊過的組件名稱,則會createComponent 建立一個組件類型的vnode

若是都不是則會建立一個位置的標籤

什麼是vnode

所謂虛擬DOM,是一個用於表示真實 DOM 結構和屬性的 JavaScript 對象,這個對象用於對比虛擬 DOM 和當前真實 DOM 的差別化,而後進行局部渲染從而實現性能上的優化。

vnode的渲染

在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函數的從響應到渲染還有他的做用的理解 有不對的地方歡迎批評指正

相關文章
相關標籤/搜索