ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

vue源码6----update函数

2020-04-21 17:53:28  阅读:288  来源: 互联网

标签:vue const oldVnode update vnode 源码 vm patch insertedVnodeQueue


在vue中,_update是最终把vnode节点渲染成真实dom的函数。

它会在首次渲染和数据更新的时候被调用。

这里主要分析首次调用时候做了什么。

_update函数在src/core/instance/lifecycle.js中定义

Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {
    const vm: Component = this
    const prevEl = vm.$el
    const prevVnode = vm._vnode
    const restoreActiveInstance = setActiveInstance(vm)
    vm._vnode = vnode
    // Vue.prototype.__patch__ is injected in entry points
    // based on the rendering backend used.
    if (!prevVnode) {
      // initial render
      vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */)
    } else {
      // updates
      vm.$el = vm.__patch__(prevVnode, vnode)
    }
    restoreActiveInstance()
    // 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.
  }

可以看到,_update的核心是调用__patch__

它在web环境中的定义src/platforms/web/runtime/index.js

// install platform patch function
Vue.prototype.__patch__ = inBrowser ? patch : noop

它指向了patch方法,这次的定义在src/platforms/web/runtime/patch.js

export const patch: Function = createPatchFunction({ nodeOps, modules })

patch接收的是createPatchFunction的返回值

这里传入了一个对象,nodeOps是一系列dom操作方法,modules定义了模块的钩子函数实现

createPatchFunction定义在src/core/vdom/patch.js中,这里patch源码较长,用到哪个贴那个吧

最终执行该函数后,返回了一个patch函数,这个函数就赋值给了vm._update里的vm.__patch__

这里只分析首次调用的情况

return function patch (oldVnode, vnode, hydrating, removeOnly) {//1.旧vnode节点,2._render生成的vnode,3.代表是否服务端渲染,4.是给transition-group用的
    if (isUndef(vnode)) {
      if (isDef(oldVnode)) invokeDestroyHook(oldVnode)
      return
    }

    let isInitialPatch = false
    const insertedVnodeQueue = []

    if (isUndef(oldVnode)) {
      // empty mount (likely as component), create new root element
      isInitialPatch = true
      createElm(vnode, insertedVnodeQueue)
    } else {
      const isRealElement = isDef(oldVnode.nodeType)
      if (!isRealElement && sameVnode(oldVnode, vnode)) {
        // patch existing root node
        patchVnode(oldVnode, vnode, insertedVnodeQueue, null, null, removeOnly)
      } else {
        if (isRealElement) {//这里是首次渲染时isRealElement是true
// mounting to a real element // check if this is server-rendered content and if we can perform // a successful hydration. if (oldVnode.nodeType === 1 && oldVnode.hasAttribute(SSR_ATTR)) { oldVnode.removeAttribute(SSR_ATTR) hydrating = true } if (isTrue(hydrating)) { if (hydrate(oldVnode, vnode, insertedVnodeQueue)) { invokeInsertHook(vnode, insertedVnodeQueue, true) return oldVnode } else if (process.env.NODE_ENV !== 'production') { warn( 'The client-side rendered virtual DOM tree is not matching ' + 'server-rendered content. This is likely caused by incorrect ' + 'HTML markup, for example nesting block-level elements inside ' + '<p>, or missing <tbody>. Bailing hydration and performing ' + 'full client-side render.' ) } } // either not server-rendered, or hydration failed. // create an empty node and replace it oldVnode = emptyNodeAt(oldVnode)//这里调用emptyNodeAt将oldVnode转换为VNode对象 } // replacing existing element const oldElm = oldVnode.elm const parentElm = nodeOps.parentNode(oldElm) // create new node createElm(//这里实际创建真实的dom对象 vnode, insertedVnodeQueue, // extremely rare edge case: do not insert if old element is in a // leaving transition. Only happens when combining transition + // keep-alive + HOCs. (#4590) 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) } // #6513 // invoke insert hooks that may have been merged by create hooks. // e.g. for directives that uses the "inserted" hook. const insert = ancestor.data.hook.insert if (insert.merged) { // start at index 1 to avoid re-invoking component mounted hook 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([oldVnode], 0, 0) } else if (isDef(oldVnode.tag)) { invokeDestroyHook(oldVnode) } } } invokeInsertHook(vnode, insertedVnodeQueue, isInitialPatch) return vnode.elm }

我们直接看createElm

function createElm (
    vnode,
    insertedVnodeQueue,
    parentElm,
    refElm,
    nested,
    ownerArray,
    index
  ) {
    if (isDef(vnode.elm) && isDef(ownerArray)) {
      // This vnode was used in a previous render!
      // now it's used as a new node, overwriting its elm would cause
      // potential patch errors down the road when it's used as an insertion
      // reference node. Instead, we clone the node on-demand before creating
      // associated DOM element for it.
      vnode = ownerArray[index] = cloneVNode(vnode)
    }

    vnode.isRootInsert = !nested // for transition enter check
    if (createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) {//这里首先会尝试创建组件
      return
    }

    const data = vnode.data
    const children = vnode.children
    const tag = vnode.tag
    if (isDef(tag)) {//这里判断Vnode是否包含tag,并对其合法性进行简单的校验
      if (process.env.NODE_ENV !== 'production') {
        if (data && data.pre) {
          creatingElmInVPre++
        }
        if (isUnknownElement(vnode, creatingElmInVPre)) {
          warn(
            'Unknown custom element: <' + tag + '> - did you ' +
            'register the component correctly? For recursive components, ' +
            'make sure to provide the "name" option.',
            vnode.context
          )
        }
      }
    //调用平台方法创建一个占位符元素
      vnode.elm = vnode.ns
        ? nodeOps.createElementNS(vnode.ns, tag)
        : nodeOps.createElement(tag, vnode)
      setScope(vnode)

      /* istanbul ignore if */
      if (__WEEX__) {
        // in Weex, the default insertion order is parent-first.
        // List items can be optimized to use children-first insertion
        // with append="tree".
        const appendAsTree = isDef(data) && isTrue(data.appendAsTree)
        if (!appendAsTree) {
          if (isDef(data)) {
            invokeCreateHooks(vnode, insertedVnodeQueue)
          }
          insert(parentElm, vnode.elm, refElm)
        }
        createChildren(vnode, children, insertedVnodeQueue)//调用createChildren创建子元素
        if (appendAsTree) {
          if (isDef(data)) {
            invokeCreateHooks(vnode, insertedVnodeQueue)//执行所有的create钩子,并push到insertedVnodeQueue中
          }
          insert(parentElm, vnode.elm, refElm)//最后调用insert方法把dom插入到父节点中
        }
      } else {
        createChildren(vnode, children, insertedVnodeQueue)
        if (isDef(data)) {
          invokeCreateHooks(vnode, insertedVnodeQueue)
        }
        insert(parentElm, vnode.elm, refElm)
      }

      if (process.env.NODE_ENV !== 'production' && data && data.pre) {
        creatingElmInVPre--
      }
    } else if (isTrue(vnode.isComment)) {//如果没传入tag,先判断是不是注释节点,创建注释vnode再插入
      vnode.elm = nodeOps.createComment(vnode.text)
      insert(parentElm, vnode.elm, refElm)
    } else {//文本节点直接插入
      vnode.elm = nodeOps.createTextNode(vnode.text)
      insert(parentElm, vnode.elm, refElm)
    }
  }

 

return function patch (oldVnode, vnode, hydrating, removeOnly) {     if (isUndef(vnode)) {       if (isDef(oldVnode)) invokeDestroyHook(oldVnode)       return     }
    let isInitialPatch = false     const insertedVnodeQueue = []
    if (isUndef(oldVnode)) {       // empty mount (likely as component), create new root element       isInitialPatch = true       createElm(vnode, insertedVnodeQueue)     } else {       const isRealElement = isDef(oldVnode.nodeType)       if (!isRealElement && sameVnode(oldVnode, vnode)) {         // patch existing root node         patchVnode(oldVnode, vnode, insertedVnodeQueue, null, null, removeOnly)       } else {         if (isRealElement) {           // mounting to a real element           // check if this is server-rendered content and if we can perform           // a successful hydration.           if (oldVnode.nodeType === 1 && oldVnode.hasAttribute(SSR_ATTR)) {             oldVnode.removeAttribute(SSR_ATTR)             hydrating = true           }           if (isTrue(hydrating)) {             if (hydrate(oldVnode, vnode, insertedVnodeQueue)) {               invokeInsertHook(vnode, insertedVnodeQueue, true)               return oldVnode             } else if (process.env.NODE_ENV !== 'production') {               warn(                 'The client-side rendered virtual DOM tree is not matching ' +                 'server-rendered content. This is likely caused by incorrect ' +                 'HTML markup, for example nesting block-level elements inside ' +                 '<p>, or missing <tbody>. Bailing hydration and performing ' +                 'full client-side render.'               )             }           }           // either not server-rendered, or hydration failed.           // create an empty node and replace it           oldVnode = emptyNodeAt(oldVnode)         }
        // replacing existing element         const oldElm = oldVnode.elm         const parentElm = nodeOps.parentNode(oldElm)
        // create new node         createElm(           vnode,           insertedVnodeQueue,           // extremely rare edge case: do not insert if old element is in a           // leaving transition. Only happens when combining transition +           // keep-alive + HOCs. (#4590)           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)               }               // #6513               // invoke insert hooks that may have been merged by create hooks.               // e.g. for directives that uses the "inserted" hook.               const insert = ancestor.data.hook.insert               if (insert.merged) {                 // start at index 1 to avoid re-invoking component mounted hook                 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([oldVnode], 0, 0)         } else if (isDef(oldVnode.tag)) {           invokeDestroyHook(oldVnode)         }       }     }
    invokeInsertHook(vnode, insertedVnodeQueue, isInitialPatch)     return vnode.elm   }

标签:vue,const,oldVnode,update,vnode,源码,vm,patch,insertedVnodeQueue
来源: https://www.cnblogs.com/idrinkmilk/p/12746259.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有