zoukankan      html  css  js  c++  java
  • Vue之组件更新

    示例1:新旧节点不同

    <template>
      <div id="app">
        <hello-world :flag="flag" />
        <button @click="toggle">toggle</button>
      </div>
    </template>
    
    <script>
    import HelloWorld from './components/HelloWorld/index'
    export default {
      name: 'App',
      components: {
        HelloWorld
      },
      data() {
        return {
          flag: true
        };
      },
      methods: {
        toggle() {
          this.flag = !this.flag;
        }
      }
    }
    </script>
    <template>
      <div class="hello" v-if="flag">
        <h1>{{ nested.msg }}</h1>
        <h2>Essential links</h2>
        <h2>ecosystem</h2>
        <ul>
          <li><a href="" target="_blank">vur-router</a></li>
          <li><a href="" target="_blank">vur-router</a></li>
          <li><a href="" target="_blank">vur-router</a></li>
          <li><a href="" target="_blank">vur-router</a></li>
          <li><a href="" target="_blank">vur-router</a></li>
        </ul>
      </div>
      <ul v-else>
        <li><a href="" target="_blank">Twitter</a></li>
        <li><a href="" target="_blank">Twitter</a></li>
        <li><a href="" target="_blank">Twitter</a></li>
        <li><a href="" target="_blank">Twitter</a></li>
        <li><a href="" target="_blank">Twitter</a></li>
      </ul>
    </template>
    
    <script>
      export default {
        name: "index",
        props: ['flag'],
        data() {
          return {
            nested: {
              mag: 'welcome to your vue.js app'
            }
          };
        }
      }
    </script>
    • 当点击toggle时,首先触发setter,然后走到App组件的渲染watcher update,在nextTick中执行flushSchedulerQueue(标记 flushing 为 true ),执行watcher.run,执行get。

    • 走到App组件的patch,会走到sameVnode

    function sameVnode (a, b) {
      return (
        a.key === b.key && (
          (
            a.tag === b.tag &&
            a.isComment === b.isComment &&
            isDef(a.data) === isDef(b.data) &&
            sameInputType(a, b)
          ) || (
            isTrue(a.isAsyncPlaceholder) &&
            a.asyncFactory === b.asyncFactory &&
            isUndef(b.asyncFactory.error)
          )
        )
      )
    }
    • 此时判断新旧节点相同,进入patchVnode,当更新的vnode是一个组件vnode的时候,会执行prepatch方法,此时会执子组件(HelloWorld组件)的prepatch钩子函数

    if (isDef(data) && isDef(i = data.hook) && isDef(i = i.prepatch)) {
       i(oldVnode, vnode)
    }
    • 该函数执行updateChildComponent,该函数对子组件数据做更新(对$attrs,$listeners,props等),更新flag的变化时,触发dep.notify(),执行子组件的update,此时由于flushing会true,渲染watcher会进入当前queue,所以父子组件的更新在一个tick中。

    • 进入到子组件的ptach,此时新旧节点不同,会进入新旧节点不同的逻辑,首先创建vode,然后执行更新父组件的占位符vnode,首先判断当前vnode是否可挂载,最后移除旧节点,插入新节点。

    示例2:新旧节点相同且都有子节点

    <template>
      <div id="app">
        <div>
          <ul>
            <li v-for="item in items" :key="item.id">{{ item.val }}</li>
          </ul>
        </div>
        <button @click="change">change</button>
      </div>
    </template>
    
    <script>
    // import HelloWorld from './components/HelloWorld/index'
    export default {
      name: 'App',
      components: {
        // HelloWorld
      },
      data() {
        return {
          items: [
            { id: 0, val: 'A'},
            { id: 1, val: 'B'},
            { id: 2, val: 'C'},
            { id: 3, val: 'D'},
          ]
        };
      },
      methods: {
        change() {
          this.items.reverse().push({ id: 4, val: 'E'});
        }
      }
    }
    </script>
    • 点击change按钮进入App组件的patch过程,此时新旧节点不同且都存在子节点进入patchVnode,分别拿新旧节点的child,进入updateChildren,此函数是组件更新核心也是最复杂的
    while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
          if (isUndef(oldStartVnode)) {
            oldStartVnode = oldCh[++oldStartIdx] // Vnode has been moved left
          } else if (isUndef(oldEndVnode)) {
            oldEndVnode = oldCh[--oldEndIdx]
          } else if (sameVnode(oldStartVnode, newStartVnode)) {
            patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue)
            oldStartVnode = oldCh[++oldStartIdx]
            newStartVnode = newCh[++newStartIdx]
          } else if (sameVnode(oldEndVnode, newEndVnode)) {
            patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue)
            oldEndVnode = oldCh[--oldEndIdx]
            newEndVnode = newCh[--newEndIdx]
          } else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved right
            patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue)
            canMove && nodeOps.insertBefore(parentElm, oldStartVnode.elm, nodeOps.nextSibling(oldEndVnode.elm))
            oldStartVnode = oldCh[++oldStartIdx]
            newEndVnode = newCh[--newEndIdx]
          } else if (sameVnode(oldEndVnode, newStartVnode)) { // Vnode moved left
            patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue)
            canMove && nodeOps.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm)
            oldEndVnode = oldCh[--oldEndIdx]
            newStartVnode = newCh[++newStartIdx]
          } else {
            if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx)
            idxInOld = isDef(newStartVnode.key)
              ? oldKeyToIdx[newStartVnode.key]
              : findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx)
            if (isUndef(idxInOld)) { // New element
              createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx)
            } else {
              vnodeToMove = oldCh[idxInOld]
              if (sameVnode(vnodeToMove, newStartVnode)) {
                patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue)
                oldCh[idxInOld] = undefined
                canMove && nodeOps.insertBefore(parentElm, vnodeToMove.elm, oldStartVnode.elm)
              } else {
                // same key but different element. treat as new element
                createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx)
              }
            }
            newStartVnode = newCh[++newStartIdx]
          }
        }
    • 该函数的核心是执行新旧vnode的diff算法,根据不同情况做不同的更新逻辑,递归调用patchVnode方法,最后执行完之后页面已经被替换成了新节点。

    总结:

    1、组件更新的过程核心就是新旧vnode diff算法,对新旧节点相同以及不同的情况分别做不同的处理。

    2、新旧节点不同的更新流程是:创建新节点=>更新父组件占位符=>删除旧节点。

    3、新旧节点相同的更新流程是去获取它们的child,根据不同情况做不同的更新逻辑。

  • 相关阅读:
    【ci框架】ci框架目录结构分析
    php CI框架
    jQuery boxy弹出层插件中文演示及讲解
    Jenkins构建报错(Jenkins is reserved for jobs with matching label expression)解决办法
    redis缓存数据架构实战
    Git免密码pull&push
    Maven搭建Nexus私有仓库
    Windows使用filezilla搭建FTP服务器
    CentOS7.4使用yum安装MySQL5.6
    MySQL数据库连接池导致页面登录无法查询问题解决过程
  • 原文地址:https://www.cnblogs.com/ltog/p/14474509.html
Copyright © 2011-2022 走看看