计算机系统应用教程网站

网站首页 > 技术文章 正文

Vue源码全面解析三十九 patchVnode函数(更新虚拟节点Vnode)

btikc 2024-10-25 10:59:23 技术文章 6 ℃ 0 评论


我们打开“src/compiler/create-compiler.js”文件,代码如下:

function patchVnode ( oldVnode,vnode, insertedVnodeQueue,ownerArray, index, removeOnly ) {
   // 节点是否相同
    if (oldVnode === vnode) {
      return
    }
		
    if (isDef(vnode.elm) && isDef(ownerArray)) {
      // 克隆节点
      vnode = ownerArray[index] = cloneVNode(vnode)
    }
    // 真实节点
    const elm = vnode.elm = oldVnode.elm

    if (isTrue(oldVnode.isAsyncPlaceholder)) {
      if (isDef(vnode.asyncFactory.resolved)) {
        hydrate(oldVnode.elm, vnode, insertedVnodeQueue)
      } else {
        vnode.isAsyncPlaceholder = true
      }
      return
    }
   
    if (isTrue(vnode.isStatic) && isTrue(oldVnode.isStatic) && vnode.key === oldVnode.key && (isTrue(vnode.isCloned) || isTrue(vnode.isOnce))) {
      vnode.componentInstance = oldVnode.componentInstance
      return
    }

    let i
    const data = vnode.data
    if (isDef(data) && isDef(i = data.hook) && isDef(i = i.prepatch)) {
      i(oldVnode, vnode)
    }
    // 旧vnode
    const oldCh = oldVnode.children
    // 新vnode
    const ch = vnode.children
    if (isDef(data) && isPatchable(vnode)) {
      // 更新属性
      for (i = 0; i < cbs.update.length; ++i) cbs.update[i](oldVnode, vnode)
      if (isDef(i = data.hook) && isDef(i = i.update)) i(oldVnode, vnode)
    }
    if (isUndef(vnode.text)) {
      if (isDef(oldCh) && isDef(ch)) {
        // 更新子集
        if (oldCh !== ch) updateChildren(elm, oldCh, ch, insertedVnodeQueue, removeOnly)
      } else if (isDef(ch)) {
        if (process.env.NODE_ENV !== 'production') {
          // 验证key
          checkDuplicateKeys(ch)
        }
        if (isDef(oldVnode.text)) nodeOps.setTextContent(elm, '')
       c
        addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue)
      } else if (isDef(oldCh)) {
        removeVnodes(oldCh, 0, oldCh.length - 1)
      } else if (isDef(oldVnode.text)) {
        nodeOps.setTextContent(elm, '')
      }
    } else if (oldVnode.text !== vnode.text) {
      nodeOps.setTextContent(elm, vnode.text)
    }
    if (isDef(data)) {
      if (isDef(i = data.hook) && isDef(i = i.postpatch)) i(oldVnode, vnode)
    }
  }

下面我还来拆分一下该代码进行分析。

 if (oldVnode === vnode) {
      return
    }

首先可以看到这里是进行了一个简单是对比,oldVnode(旧) 等于 vnode 的话,那就直接返回了。

 if (isDef(vnode.elm) && isDef(ownerArray)) {
   // clone reused vnode
   vnode = ownerArray[index] = cloneVNode(vnode)
 }

vnode.elm属性存在,说明该节点已被渲染过,ownerArray存在于子元素更新的时候。

如果两者都存在的话,那就调用 “cloneVNode” 函数,复制一个节点出来。

 if (isTrue(vnode.isStatic) && 
     isTrue(oldVnode.isStatic) && 
     vnode.key === oldVnode.key &&
     (isTrue(vnode.isCloned) || isTrue(vnode.isOnce))
    ) {
   vnode.componentInstance = oldVnode.componentInstance
   return
 }

可以看到该语句赋值了 “componentInstance” 属性之后就直接返回了。

需满足以下条件:

1、vnode必须是 isStatic (静态节点)

2、oldVnode必须是 isStatic (静态节点)

3、vnode和oldVnode 的key值必须相等

4、vnode是克隆的vnode节点或者是一次性节点(isOnce存在)

let i
const data = vnode.data 
if (isDef(data) && isDef(i = data.hook) && isDef(i = i.prepatch)) {
   i(oldVnode, vnode)
 }

这里主要是看是否存在“hook” 属性。

组件才会存在hook属性,有 init、prepatch、insert、destroy四个hook(钩子属性)

init 初始化组件

prepatch 更新组件

insert 插入到DOM原生中

destroy 销毁组件

const oldCh = oldVnode.children
const ch = vnode.children
if (isDef(data) && isPatchable(vnode)) {
  for (i = 0; i < cbs.update.length; ++i) cbs.update[i](oldVnode, vnode)
  if (isDef(i = data.hook) && isDef(i = i.update)) i(oldVnode, vnode)
}

这里主要是执行更新操作,isPatchable 函数用于验证vnode是否存在 tag属性。

注释节点和纯文本节点不存在tag属性

update为数组,主要更新如下内容:

1、updateAttrs // 更新attr属性

2、updateClass // 更新class属性

3、updateDOMListeners // 更新事件

4、updateDOMProps // 更新props

5、updateStyle // 更新style

6、update // 更新引用

7、updateDrectives // 更新指令

// text属性不存在
if (isUndef(vnode.text)) {
   if (isDef(oldCh) && isDef(ch)) {
     // 更新子集vnode
     if (oldCh !== ch) updateChildren(elm, oldCh, ch, insertedVnodeQueue, removeOnly)
   } else if (isDef(ch)) {
     if (process.env.NODE_ENV !== 'production') {
       // 验证重复的key
       checkDuplicateKeys(ch)
     }
     // 设置真实节点是文本值
     if (isDef(oldVnode.text)) nodeOps.setTextContent(elm, '')
     // 添加新的vnode节点
     addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue)
   } else if (isDef(oldCh)) {
     // 移除vnode节点
     removeVnodes(oldCh, 0, oldCh.length - 1)
   } else if (isDef(oldVnode.text)) {
     // 设置真实节点是文本值
     nodeOps.setTextContent(elm, '')
   }
 } else if (oldVnode.text !== vnode.text) {
    // 设置真实节点是文本值
   nodeOps.setTextContent(elm, vnode.text)
 }

以上代码最陌生的应该是 nodeOps 对象了,我们来看看该对象具有哪些功能:

1、createElement // 创建元素

2、createElementNS // 创建指定命名空间元素

3、createTextNode // c文本元素

4、createComment // // 创建注释元素

5、insertBefore // 插入元素之前

6、removeChild // 移除子节点

7、appendChild // 添加子节点

8、parentNode // 父级元素

9、nextSibling // 下一个兄弟元素

10、tagName // 标签名称

11、setTextContent // 设置文本内容

12、setStyleScope // 设置内联样式

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表