vue2.x源码解析五——数据驱动--4.render

本文详细探讨了Vue2.x中的数据驱动原理,重点解析了`vm._render`方法的实现,包括调用`render`函数、`$createElement`以及`_renderProxy`的作用。同时,解释了Virtual DOM的概念,原生DOM的代价以及Vue中Virtual DOM的创建和优化策略。文章还介绍了`createElement`方法,尤其是对children的规范化处理,确保所有子节点都转换为VNode,最终形成VNode Tree,完整地描述了DOM Tree。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.连接上一节

在上一节中我们提到了在src/core/instance/lifecycle.js 中通过渲染Watcher实时去监测调用updateComponent方法,从而实现的页面实时渲染,vm._render()主要是生成的VNode(虚拟DOM),下面我们来讲一讲vm._render()方法的实现

updateComponent = () => {
      vm._update(vm._render(), hydrating)
    }

2.vm._render

Vue 的 _render 方法是实例的一个私有方法,它用来把实例渲染成一个虚拟 Node。 src/core/instance/render.js

Vue.prototype._render = function (): VNode {
   
  const vm: Component = this
  const { render, _parentVnode } = vm.$options

  // reset _rendered flag on slots for duplicate slot check
  if (process.env.NODE_ENV !== 'production') {
    for (const key in vm.$slots) {
      // $flow-disable-line
      vm.$slots[key]._rendered = false
    }
  }

  if (_parentVnode) {
    vm.$scopedSlots = _parentVnode.data.scopedSlots || emptyObject
  }

  // set parent vnode. this allows render functions to have access
  // to the data on the placeholder node.
  vm.$vnode = _parentVnode
  // render self
  let vnode
  try {
    vnode = render.call(vm._renderProxy, vm.$createElement)
  } catch (e) {
    handleError(e, vm, `render`)
    // return error render result,
    // or previous vnode to prevent render error causing blank component
    /* istanbul ignore else */
    if (process.env.NODE_ENV !== 'production') {
      if (vm.$options.renderError) {
        try {
          vnode = vm.$options.renderError.call(vm._renderProxy, vm.$createElement, e)
        } catch (e) {
          handleError(e, vm, `renderError`)
          vnode = vm._vnode
        }
      } else {
        vnode = vm._vnode
      }
    } else {
      vnode = vm._vnode
    }
  }
  // return empty vnode in case the render function errored out
  if (!(vnode instanceof VNode)) {
    if (process.env.NODE_ENV !== 'production' && Array.isArray(vnode)) {
      warn(
        'Multiple root nodes returned from render function. Render function ' +
        'should return a single root node.',
        vm
      )
    }
    vnode = createEmptyVNode()
  }
  // set parent
  vnode.parent = _parentVnode
  return vnode
}

2.1 - 步骤1

拿到vue实例参数中的render函数,这个render函数可以使用户自己写的,也可以是template编译成的render函数

const { render, _parentVnode } = vm.$options

2.2 - 步骤2

去调用这个render函数

 try {
      vnode = render.call(vm._renderProxy, vm.$createElement)
    } catch (e) {
    }

2.2.2 vm.$createElement

创建虚拟的DOM
定义在

export function initRender (vm: Component) {
   
     // 编译时创建VNode的方法
      vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)
      // 手写render函数的时候创建VNode的方法
      vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)
}

initRender这个函数在new Vue的时候会去调用在src/core/instance/init.js中可以看到

initRender(vm)

对于vm.$createElement我们可举一个例子
例如:

<div id="app"></div>
const vm = new Vue({
    el: '#app',
    render(createElement) {
        return createElement('div', {
            attrs: {
                id: '#hcd'
            }
        },this.message)
    },
    data() {
        return {
            message: 'this vue'
        }
    }
})

会发现render产生的

<div id="hcd">this vue</div>

会覆盖掉

<div id="app"></div>

页面中没有了id为app的div
这也是我们不能将vue实例挂载在body和html的原因,因为会覆盖

并且比

<div id="app">{
   {
   msg}}</div>

加载的更优化,这是因为,dom是先与js加载的,所以会先看到{ {msg}}显示在页面上,而render的方式会在vue实例加载后直接显示数据在页面上

2.2.2 vm._

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值