vm._update(vm.render())

260 阅读1分钟

vm.render()

  • 用于把实例渲染成一个虚拟vnode

Vue.prototype._render 会执行如下

vnode = render.call(vm._renderProxy, vm.$createElement)

initRender (vm: Component) {
  vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)
}

export function _createElement (
  context: Component,
  tag?: string | Class<Component> | Function | Object,
  data?: VNodeData,
  children?: any,
  normalizationType?: number
){
   children = normalizeChildren(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 (isDef(Ctor = resolveAsset(context.$options, 'components', tag))) {
      // component
      vnode = createComponent(Ctor, data, context, children, tag)
    } else {
      vnode = new VNode(
        tag, data, children,
        undefined, undefined, context
      )
    }
  } else {
    vnode = createComponent(tag, data, context, children)
  }
  
  return vnode

vm._update()

  • 用于把vnode渲染为真实的dom

Vue.prototype._update 方法执行
vm.el = vm.__patch__(vm.el, vnode)执行
patch= createPatchFunction({ nodeOps, modules })

const hooks = ['create', 'activate', 'update', 'remove', 'destroy']

export function createPatchFunction (backend) {
  let i, j
  const cbs = {}

  const { modules, nodeOps } = backend

  for (i = 0; i < hooks.length; ++i) {
    cbs[hooks[i]] = []
    for (j = 0; j < modules.length; ++j) {
      if (isDef(modules[j][hooks[i]])) {
        cbs[hooks[i]].push(modules[j][hooks[i]])
      }
    }
  }

  // ...

  return function patch (oldVnode, vnode, hydrating, removeOnly) {
    if (isUndef(vnode)) {
      if (isDef(oldVnode)) invokeDestroyHook(oldVnode)
      return
    }

    let isInitialPatch = false
    const insertedVnodeQueue = []

    if (isUndef(oldVnode)) {
      isInitialPatch = true
      createElm(vnode, insertedVnodeQueue)
    } else {
      const isRealElement = isDef(oldVnode.nodeType)
      if (!isRealElement && sameVnode(oldVnode, vnode)) {
        patchVnode(oldVnode, vnode, insertedVnodeQueue, removeOnly)
      } else {
        if (isRealElement) {
          oldVnode = emptyNodeAt(oldVnode)
        }
        const oldElm = oldVnode.elm
        const parentElm = nodeOps.parentNode(oldElm)

        // create new node
        createElm(
          vnode,
          insertedVnodeQueue,
          oldElm._leaveCb ? null : parentElm,
          nodeOps.nextSibling(oldElm)
        )

        // update parent placeholder node element, recursively
        if (isDef(vnode.parent)) {
          let ancestor = vnode.parent
          const patchable = isPatchable(vnode)
          while (ancestor) {
            for (let i = 0; i < cbs.destroy.length; ++i) {
              cbs.destroy[i](ancestor)
            }
            ancestor.elm = vnode.elm
            if (patchable) {
              for (let i = 0; i < cbs.create.length; ++i) {
                cbs.create[i](emptyNode, ancestor)
              }
              const insert = ancestor.data.hook.insert
              if (insert.merged) {
                for (let i = 1; i < insert.fns.length; i++) {
                  insert.fns[i]()
                }
              }
            } else {
              registerRef(ancestor)
            }
            ancestor = ancestor.parent
          }
        }

        // destroy old node
        if (isDef(parentElm)) {
          removeVnodes(parentElm, [oldVnode], 0, 0)
        } else if (isDef(oldVnode.tag)) {
          invokeDestroyHook(oldVnode)
        }
      }
    }

    invokeInsertHook(vnode, insertedVnodeQueue, isInitialPatch)
    return vnode.elm
  }
}

diff算法

1.关于虚拟dom 结构

代码地址: github.com/chengliu050…

  <div id="div1" class="container">
     <p>hello</p>
     <ul style="font-size:16px;">
        <li><hello/li>
     </ul>
  </div>

对应vdom结构如下

   {
       tag:'div',
       props:{
           className:'container',
           id:'div'
       }
       children:[
            {
                tag:'p',
                children:'hello'
            }
            {
                tag:'ul',
                props:{
                    style:'font-size:16px'
                },
                children:[
                    {
                        tag:'li',
                        children:'a'
                    }
                ]
            }
       ]
   }

2.diff算法产生的补丁patch主要分为remove,text,attr,replace

## vdom diff算法大概遵守以下几点
 * 只比较同一层级,不跨级比较
 * tag不同则直接remove,不再深度遍历
 * tag和key相同,则认为相同节点,不再深度遍历     

 1. 无新节点,{typeremove}
 2. 有新旧节点,并且Node为string且不相同,{type:text,text:'newNode'}
 3. 新旧节点的type相同,比较attribute{type:attr,{key1:'',key2:''}
    遍历children

3.patch补丁更新