Vue2.x原码解析-虚拟DOM

虚拟DOM的概念
  虚拟DOM(Virtual DOM)是使用js对象描述真实DOM。通过对比变化前后的虚拟DOM,可以精确定位到视图的变化,只修改变化部分的视图,提高复杂视图变化时的渲染效率。

Vue中的虚拟DOM
  Vue中的虚拟DOM借鉴了Snabbdom,其模块机制,钩子函数以及diff算法和Snabbdom几乎一致,但Snabbdom的基础上添加了Vue的特性,如指令和组件机制。

虚拟DOM的优缺点
优点

  1. 避免直接操作DOM,提交开发效率。
  2. 作为中间层可以实现跨平台。
  3. 复杂视图情况下可以提升渲染性能

缺点

  1. 首次渲染会增加开销,因为要维护额外的虚拟DOM
  2. 简单视图情况下会增加额外的成本。在理想状态下,每次数据的修改都能直接定位到视图的修改,这样比使用虚拟DOM效率更高,消耗的资源更少。

render函数中的参数h函数
  render函数中的参数h函数和Snabbdom中的h函数是类似的,用来创建虚拟DOM,接收四个参数,前三个参数和Snabbdom中的h函数一致,第一个是字符串类型的标签或者选择器,第二个参数是一个可选的选项对象,第三个参数是表示子元素,可以是一个字符串或一个数组,也是可选的。

const vm = new Vue({
   
  el: '#app',
  render (h) {
   
    // h(tag, data, children)
    // return h('h1', this.msg)
    // return h('h1', { domProps: { innerHTML: this.msg } })
    // return h('h1', { attrs: { id: 'title' } }, this.msg)
    const vnode = h(
      'h1', 
      {
    
        attrs: {
    id: 'title' } 
      },
      this.msg
    )
    console.log(vnode)
    return null
  },
  data: {
   
    msg: 'Hello Vue'
  }
})

h函数即vm.$createElement方法。
h函数最终返回一个VNode对象(即虚拟DOM对象)。包括以下核心属性:

  1. tag:调用h函数传入的第一个参数,表示要创建节点的选择器。
  2. data:调用h函数传入的第二个参数,描述节点的属性对象。
  3. children:调用h函数传入的第三个参数,表示节点的子元素信息。
  4. text:节点的文本内容。
  5. elm:节点对应的真实DOM,节点转化为真实DOM之后将保存在该属性。
  6. key:节点标记,为了复用节点。

VNode的创建过程
  即createElment的调用过程:

  1. mountComponent中定义了updateComponent,创建Watcher时会将updateComponent传入,数据变化时会调用updateComponent重新渲染。而updateComponent内部则是调用了vm._render方法。
    在这里插入图片描述

  2. _render方法时在instance/render.js文件中定义的。方法内部是通过调用用户传入的render方法来创建虚拟DOM对象。调用render方法时会传入vm.$createElement(即h函数)作为参数

    //vm._renderProxy = vm
    vnode = render.call(vm._renderProxy, vm.$createElement)
    
  3. vm.$createElement内部调用createElement方法,其内部将调用_createElement方法生成虚拟DOM_createElement方法实现如下:

    export function _createElement (
      context: Component,
      tag?: string | Class<Component> | Function | Object,
      data?: VNodeData,
      children?: any,
      normalizationType?: number
    ): VNode | Array<VNode> {
         
      if (isDef(data) && isDef((data: any).__ob__)) {
         
        process.env.NODE_ENV !== 'production' && warn(
          `Avoid using observed data object as vnode data: ${
           JSON.stringify(data)}\n` +
          'Always create fresh vnode data objects in each render!',
          context
        )
        return createEmptyVNode()
      }
      // <component v-bind:is="currentTabComponent"></component>
      // object syntax in v-bind
      if (isDef(data) && isDef(data.is)) {
         
        tag = data.is
      }
      if (!tag) {
         
        // in case of component :is set to falsy value
        return createEmptyVNode()
      }
      // warn against non-primitive key
      if (process.env.NODE_ENV !== 'production' &&
        isDef(data) && isDef(data.key) && !isPrimitive(data.key)
      ) {
         
        if (!__WEEX__ || !('@binding' in data.key)) {
         
          warn(
            'Avoid using non-primitive value as key, ' +
            'use string/number value instead.',
            context
          )
        }
      }
      // support single function children as default scoped slot
      if (Array.isArray(children) &&
        typeof children[0] === 'function'
      ) {
         
        data = data || {
         }
        data.scopedSlots = {
          default: children[0] }
        children.length = 0
      }
      if (normalizationType === ALWAYS_NORMALIZE) {
          //用户传入的render函数
        // 返回一维数组,处理用户手写的 render
        children = normalizeChildren(children)
      } else if (normalizationType === SIMPLE_NORMALIZE) {
         
        // 把二维数组,转换成一维数组
        children = simpleNormalizeChildren(children)
      }
      
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值