Vue 源碼解析(六)之 vm._render

_render函數(shù)

  • 上一講我們分析到調(diào)用了vm._update(vm._render(), hydrating),那么這一講我們就先分析 vm._render() 做了什么
  • 首先 _render 方法是在哪里定義的呢?在core/instance/render.js
Vue.prototype._render = function (): VNode {
    const vm: Component = this
    const { render, _parentVnode } = vm.$options
    
    if (_parentVnode) {
      vm.$scopedSlots = normalizeScopedSlots(
        _parentVnode.data.scopedSlots,
        vm.$slots,
        vm.$scopedSlots
      )
    }
    
    
    vm.$vnode = _parentVnode
        
    let vnode
    try {
      currentRenderingInstance = vm
      vnode = render.call(vm._renderProxy, vm.$createElement)
    } catch (e) {
      handleError(e, vm, `render`)
      
      vnode = vm._vnode
    } finally {
      currentRenderingInstance = null
    }
    if (Array.isArray(vnode) && vnode.length === 1) {
      vnode = vnode[0]
    }
        
    if (!(vnode instanceof VNode)) {
      vnode = createEmptyVNode()
    }
    vnode.parent = _parentVnode
    return vnode
}
  • 我們可以看到const { render, _parentVnode } = vm.$options先從 $options 中拿到了render

  • 然后執(zhí)行了 render,并傳參 vm.$createElement, 從而獲取到 vnode 并返回

// vm._renderProxy就是vm,忽略不分析
vnode = render.call(vm._renderProxy, vm.$createElement)

vnode

  • dom元素是非常龐大的,我們頻繁去做dom更新就會(huì)產(chǎn)生一定的性能問題
  • Vnode 就是用原生的 js 對(duì)象去描述 DOM 節(jié)點(diǎn),它借鑒了開源庫(kù)snabbdom ,在 flow 文件夾下可以看到 VNode 的定義,,對(duì) vnode 定義了一些關(guān)鍵屬性如標(biāo)簽名、數(shù)據(jù)、子節(jié)點(diǎn)等,用來映射到真實(shí) DOM 的渲染,因此輕量簡(jiǎn)單
  • Vnode 映射到真實(shí) DOM 需要經(jīng)歷創(chuàng)建 diff patch等過程
  • Vnode 是如何創(chuàng)建的呢? 就是通過我們之前分析的 vm.$createElement 函數(shù)

vm.$createElement 是什么

  • 在 function Vue 階段做了各種 mixin , 其中有 initRender, initRender 中包括這樣一段代碼
vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)
  • 也就是說我們的 vm.$createElement 實(shí)際上是去執(zhí)行了 createElement 操作

createElement

  • core/vdom/create-element.js
export function createElement (
  context: Component, // vm 實(shí)例
  tag: any, // 標(biāo)簽
  data: any, // 數(shù)據(jù)
  children: any, // 子節(jié)點(diǎn),從而構(gòu)建出 vnode tree
  normalizationType: any,
  alwaysNormalize: boolean
): VNode | Array<VNode> {
  if (Array.isArray(data) || isPrimitive(data)) {
    normalizationType = children
    // 沒有 data 時(shí)參數(shù)前移
    children = data
    data = undefined
  }
  if (isTrue(alwaysNormalize)) {
    normalizationType = ALWAYS_NORMALIZE
  }
  return _createElement(context, tag, data, children, normalizationType)
}
  • 我們發(fā)現(xiàn)它實(shí)際上是對(duì)參數(shù)做了一層封裝,然后調(diào)用 _createElement 方法

_createElement

  • 我把這部分的代碼精簡(jiǎn)了一下,保留了核心的部分
export function _createElement (
  context: Component,
  tag?: string | Class<Component> | Function | Object,
  data?: VNodeData,
  children?: any,
  normalizationType?: number
): VNode | Array<VNode> {
  if (normalizationType === ALWAYS_NORMALIZE) {
    children = normalizeChildren(children)
  } else if (normalizationType === SIMPLE_NORMALIZE) {
    children = simpleNormalizeChildren(children)
  }
  let vnode, ns
  if (typeof tag === 'string') {
    let Ctor
    ns = (context.$vnode && context.$vnode.ns) || config.getTagNamespace(tag)
    if (config.isReservedTag(tag)) {
      vnode = new VNode(
        config.parsePlatformTagName(tag), data, children,
        undefined, undefined, context
      )
    } else if ((!data || !data.pre) && isDef(Ctor = resolveAsset(context.$options, 'components', tag))) {
      vnode = createComponent(Ctor, data, context, children, tag)
    } else {
      vnode = new VNode(
        tag, data, children,
        undefined, undefined, context
      )
    }
  } else {
    vnode = createComponent(tag, data, context, children)
  }
  if (Array.isArray(vnode)) {
    return vnode
  } else if (isDef(vnode)) {
    if (isDef(ns)) applyNS(vnode, ns)
    if (isDef(data)) registerDeepBindings(data)
    return vnode
  } else {
    return createEmptyVNode()
  }
}
  • 我們會(huì)發(fā)現(xiàn)這個(gè)函數(shù)先對(duì) children 做了 normalize, 因?yàn)閏hildren 可能是數(shù)組類型,normalize方法,normalizeArrayChildren 主要的邏輯是遍歷 children,獲得單個(gè)節(jié)點(diǎn) c, 然后判斷 c 的類型,如果是數(shù)組類型,則遞歸調(diào)用 normalizeArrayChildren,如果是基礎(chǔ)類型,則通過 createTextVNode 轉(zhuǎn)化為 VNode 類型(其中做了優(yōu)化,兩個(gè)連續(xù)的 text 節(jié)點(diǎn)會(huì)合并成一個(gè) text 節(jié)點(diǎn)),變成了一個(gè)一維數(shù)組
  • 我們現(xiàn)在 demo 的 tag 是 string 類型,因此會(huì)走到 vnode = new Vnode()從而生成一個(gè) vnode

demo 調(diào)試

  • 調(diào)用 _createElement
  • _createElement 創(chuàng)建了vnode
  • 因此 _render 最終返回了一個(gè) vnode
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容