vue render函數進階學習


上一篇博客我和大家分享了vue render函數的基礎使用

這篇博客我們來簡單講一講render函數他是怎么實現得

先來一張官方得圖

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

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

render函數觸發的時機

剛才在上邊已經說過了,render函數會在響應數據發生改變的時候去被觸發,那么就不得不說一說vue的響應式原理

之前就聽說過vue實現的原理是數據劫持,那么他到底是怎么做到的數據劫持這件事呢?

在vue的源碼里邊可以看到,vue實例在初始化的時候會把data里邊所有的屬性添加一個對象進去

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函數 更新dom

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM