vue 페이지 업데이트 패 치 구현 예제

13961 단어 vue업데이트patch
patch 프로 세 스
구성 요소 페이지 렌 더 링 시 render 가 되 돌아 오 는 새 vnode(새 노드)와 구성 요소 인 스 턴 스 가 저 장 된 vnode(옛 노드)를 매개 변수 로 하여 patch 방법 을 호출 하여 DOM 을 업데이트 합 니 다.
두 노드 가 같 는 지 판단 하 다.
처리 과정 에서 노드 가 같 는 지 판단 해 야 한다.같은 노드 는 다음 과 같은 조건 을 만족 시 켜 야 한다.
  • 키 가 같 음
  • 라벨 유형 이 같 음
  • 주석 노드 의 표지 가 똑 같 고 모두 주석 노드 이거 나 주석 노드
  • 가 아니다.
  • data 의 값 상태 가 같 거나 값 이 있 거나 없 거나
  • 
    function sameVnode (a, b) {//     VNode          
      return (
        a.key === b.key && // key  
        (
          a.tag === b.tag && // tag  
          a.isComment === b.isComment && //         
          isDef(a.data) === isDef(b.data) && // data     
          sameInputType(a, b) // input type  
        )
      )
    }
    patch 방법
    patch 판단 절 차 는 다음 과 같 습 니 다.
    a)새 노드 가 비어 있 으 면 오래된 노드 가 존재 합 니 다(구성 요소 가 삭 제 될 때).오래된 노드 destroy 생명주기 함 수 를 호출 합 니 다.
    b)오래된 노드 가 비어 있 으 면 새 노드 에 따라 DOM 을 만 듭 니 다.
    c)기타(신 구 노드 가 모두 존재 한다 면)
  • a)오래된 노드 는 DOM(구성 요소 노드)이 아니 라 신 구 노드 가 같다.
  • patchVnode
  • 실행
  • b)옛 노드 는 DOM 요소 이거 나 두 노드 가 다르다.
  • 새 노드 DOM 을 만 들 고 오래된 노드 와 DOM 을 소각 합 니 다.
  • 
    function patch (oldVnode, vnode, hydrating, removeOnly) {
      if (isUndef(vnode)) {
       if (isDef(oldVnode)) { invokeDestroyHook(oldVnode); }
       return
      }
      ...
      if (isUndef(oldVnode)) {
       isInitialPatch = true;//       
       createElm(vnode, insertedVnodeQueue);
      } else {
       var isRealElement = isDef(oldVnode.nodeType);
       if (!isRealElement && sameVnode(oldVnode, vnode)) {
        patchVnode(oldVnode, vnode, insertedVnodeQueue, null, null, removeOnly);
       } else {
        ...
        var oldElm = oldVnode.elm;
        var parentElm = nodeOps.parentNode(oldElm);//      
        // create new node
        createElm(
         vnode,
         insertedVnodeQueue,
         oldElm._leaveCb ? null : parentElm,
         nodeOps.nextSibling(oldElm)//          
        );
        if (isDef(parentElm)) {
         removeVnodes(parentElm, [oldVnode], 0, 0);//        DOM  
        } else if (isDef(oldVnode.tag)) {
         invokeDestroyHook(oldVnode);
        }
       }
      }
      invokeInsertHook(vnode, insertedVnodeQueue, isInitialPatch);
      return vnode.elm
     }
    }
    patchVnode 방법
    두 노드 가 동시에 patchVnode 방법 을 실행 합 니 다.각종 상황 을 처리 하기 전에 낡은 노드 의 elm 속성 값 을 새 노드 의 elm 속성 에 부여 하고 elm 를 일치 하 게 유지 합 니 다.
    구체 적 인 절 차 는 다음 과 같다.
    a)신 구 노드 가 완전히 같다 면(같은 oldVnode==vnode 참조)
  • 직접 되 돌려 처리 하지 않 음
  • b)새 노드 가 텍스트 노드 가 아니라면
  • a)모두 하위 노드 가 존재 하고 신 구 노드 의 하위 노드 배열 은 서로 다르다(oldCh!==ch)
  • updateChildren
  • b)새로운 노드 는 하위 노드 가 있 고 오래된 노드 는 없다.
  • 1)중성자 노드 검사(key)
  • 2)오래된 노드 가 텍스트 노드 라면 텍스트 를 비 웁 니 다
  • 3)하위 노드 DOM 요소 만 들 기
  • c)옛 노드 는 하위 노드 가 있 고 새로운 노드 는 없다.
  • 하위 노드 및 DOM 제거
  • d)옛 노드 는 텍스트 노드 이다.
  • 텍스트 지우 기
  • c)새 노드 가 텍스트 노드 이 고 이전 노드 텍스트 와 다르다 면
  • 텍스트 내용 을 직접 바 꿉 니 다.
  • d)기타(새 노드 는 텍스트 노드 이 고 옛 노드 와 같다)
  • 처리 하지 않 음
  • 
     function patchVnode (
      oldVnode,
      vnode,
      insertedVnodeQueue,
      ownerArray,
      index,
      removeOnly
     ) {
      if (oldVnode === vnode) {
       return
      }
      ...
      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') {
         checkDuplicateKeys(ch);
        }
        if (isDef(oldVnode.text)) { nodeOps.setTextContent(elm, ''); }
        addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue);
       } else if (isDef(oldCh)) {
        removeVnodes(elm, oldCh, 0, oldCh.length - 1);
       } else if (isDef(oldVnode.text)) {
        nodeOps.setTextContent(elm, '');
       }
      } else if (oldVnode.text !== vnode.text) {
       nodeOps.setTextContent(elm, vnode.text);
      }
      ...
     }
    updateChildren 방법
    updateChildren 방법 은 같은 신 구 노드 의 하위 노드 를 처리 합 니 다.방법 은 다음 변 수 를 정의 합 니 다(updateChildren 의 노드 는 모두 하위 노드 를 표시 합 니 다).
    
      var oldStartIdx = 0;//                 
      var newStartIdx = 0;//                 
      var oldEndIdx = oldCh.length - 1;//                 
      var oldStartVnode = oldCh[0];//               
      var oldEndVnode = oldCh[oldEndIdx];//               
      var newEndIdx = newCh.length - 1;//                 
      var newStartVnode = newCh[0];//               
      var newEndVnode = newCh[newEndIdx];//               
      var oldKeyToIdx, //         key   
        idxInOld, //     key         
        vnodeToMove, //     key       
        refElm;//                     (   ) DOM  
    신 구 노드 의 비교 결과 에 따라 DOM 요 소 를 업데이트 합 니 다.이 과정 은 신 구 노드 의 순 서 를 바 꾸 지 않 습 니 다.번 호 는 처리 중인 노드 를 가리 키 는데 각각 신 구 노드 의 시작 과 끝 노드 이다.대비 과정 은 새로운 시작 노드 를 주도 로 하고 대비 방향 은 양측 에서 중간 으로 한다.우선 신 구 노드 의 시작 노드 와 끝 노드 를 비교 하고 새로운 시작 노드 와 같 고 처리 되 지 않 은 구 노드 를 찾 습 니 다.오래된 노드 가 모두 처리 되 었 을 때(오래된 시작 과 끝 번호 가 겹 친다)새 노드 가 처리 되 지 않 았 을 때 새 노드 DOM 요 소 를 추가 할 수 있 습 니 다.새 노드 가 모두 처 리 될 때(새 시작 과 끝 번호 가 겹 칠 때)오래된 노드 가 존재 할 수 있 으 면 오래된 노드 DOM 요 소 를 삭제 합 니 다.
    구체 적 인 절 차 는 다음 과 같다.
    신 구 서브 노드 의 시작 번호 가 끝 번호 보다 크 지 않 을 때 다음 절 차 를 실행 합 니 다.
    a)오래된 하위 노드 양쪽 에 노드 가 존재 한다 면undefined노드
  • 낡은 시작 노드undefined,oldStartVnode = oldCh[++oldStartIdx]
  • 옛 엔 딩 노드undefined,oldEndVnode = oldCh[--oldEndIdx]
  • b)신 구 서브 노드 의 시작 노드 가 같다(앞 뒤 비교)
  • patchVNodeDOM 콘 텐 츠 업데이트
  • oldStartVnode = oldCh[++oldStartIdx]
  • newStartVnode = newCh[++newStartIdx]
  • c)신 구 서브 노드 의 끝 노드 가 같다(앞 뒤 비교)
  • patchVNodeDOM 콘 텐 츠 업데이트
  • oldEndVnode = oldCh[--oldEndIdx]
  • newEndVnode = newCh[--newEndIdx]
  • d)낡은 시작 노드 와 새 끝 노드 가 같다(앞 뒤 비교)
  • patchVNodeDOM 콘 텐 츠 업데이트
  • 오래된 시작 노드 DOM 을 오래된 끝 노드 DOM 앞 에 추가 합 니 다
  • oldStartVnode = oldCh[++oldStartIdx]
  • newEndVnode = newCh[--newEndIdx]
  • e)옛 엔 딩 노드 와 새로운 시작 노드 가 같 음(앞 뒤 비교)
  • patchVNodeDOM 콘 텐 츠 업데이트
  • 오래된 엔 딩 노드 DOM 을 오래된 시작 노드 DOM 앞 에 추가 합 니 다
  • oldEndVnode = oldCh[--oldEndIdx]
  • newStartVnode = newCh[++newStartIdx]
  • f)기타(캐 시 처리 되 지 않 은 오래된 노드 key 값 으로 이전 노드 에 새 시작 노드 와 같은 노드 가 존재 하 는 지 판단 합 니 다)
  • a)처리 되 지 않 은 오래된 노드 에는 새로운 시작 노드 와 같은 노드 가 존재 하지 않 습 니 다.
  • 새 노드 DOM 을 만 들 고 이전 시작 노드 DOM 앞 에 추가 합 니 다
  • newStartVnode = newCh[++newStartIdx]
  • b)옛 노드 에 새로운 시작 노드 key 와 같은 노드 가 존재 합 니 다.
  • a)옛 노드 에 새로운 시작 노드 와 같은 노드 가 존재 한다.
  • patchVode
  • 같은 오래된 노드 DOM 을 오래된 시작 노드 DOM 앞 에 추가 합 니 다
  • 같은 옛 노드 를 undefinedoldCh[idxInOld] = undefined
  • 로 설정 합 니 다.
  • newStartVnode = newCh[++newStartIdx]
  • b)key 는 같 지만 태그 유형 이 다른 노드
  • 새 노드 DOM 을 만 들 고 이전 시작 노드 DOM 앞 에 추가 합 니 다
  • newStartVnode = newCh[++newStartIdx]
  • 순환 종료
    a)오래된 노드 가 다 옮 겨 다 니 면oldStartIdx > oldEndIdx
  • 나머지 처리 되 지 않 은 새 노드 DOM 을 이전 새 엔 딩 노드 DOM 앞 에 추가 합 니 다(새로운 시작 노드 부터 새 엔 딩 노드 까지 처리 되 지 않 았 습 니 다)
  • b)새 노드 가 다 옮 겨 다 니 면newStartIdx > newEndIdx
  • 낡은 시작 과 끝 노드,그리고 그들 사이 의 노드 를 제거 하 는 DOM(낡은 시작 노드 부터 낡은 끝 노드 까지 처 리 된 노드 가 존재 할 수 있 지만 처 리 된 것 은 undefined)
  • 
    function updateChildren (parentElm, oldCh, newCh, insertedVnodeQueue, removeOnly) {
      var oldStartIdx = 0;//                 
      var newStartIdx = 0;//                 
      var oldEndIdx = oldCh.length - 1;//                 
      var oldStartVnode = oldCh[0];//               
      var oldEndVnode = oldCh[oldEndIdx];//               
      var newEndIdx = newCh.length - 1;//                 
      var newStartVnode = newCh[0];//               
      var newEndVnode = newCh[newEndIdx];//               
      var oldKeyToIdx, idxInOld, vnodeToMove, refElm;
      ...
      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, newCh, newStartIdx);
        oldStartVnode = oldCh[++oldStartIdx];
        newStartVnode = newCh[++newStartIdx];
       } else if (sameVnode(oldEndVnode, newEndVnode)) {
        patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue, newCh, newEndIdx);
        oldEndVnode = oldCh[--oldEndIdx];
        newEndVnode = newCh[--newEndIdx];
       } else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved right
        patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue, newCh, newEndIdx);
        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, newCh, newStartIdx);
        canMove && nodeOps.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm);
        oldEndVnode = oldCh[--oldEndIdx];
        newStartVnode = newCh[++newStartIdx];
       } else {
        if (isUndef(oldKeyToIdx)) { oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx); }//           key 
        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, newCh, newStartIdx);
          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];
       }
      }
      if (oldStartIdx > oldEndIdx) {
       refElm = isUndef(newCh[newEndIdx + 1]) ? null : newCh[newEndIdx + 1].elm;
       addVnodes(parentElm, refElm, newCh, newStartIdx, newEndIdx, insertedVnodeQueue);
      } else if (newStartIdx > newEndIdx) {
       removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx);
      }
     }
    updateChildren 의 예제:
    1.왼쪽 은 신 구 노드 를 나타 내 고 노드 아래 에 표지 의 시작 과 끝 노드(즉 처리 중인 노드)를 나타 낸다.오른쪽 은 현재 DOM 을 표시 합 니 다.

    2.새로운 노드 의 시작 과 끝 노드 는 구 노드 의 시작 과 끝 노드 가 서로 다 르 고 구 노드 에서 새로운 시작 노드(새 노드 f)와 같은 노드 를 찾 지 못 한다.
    그래서 노드 f 의 DOM 을 만 들 고 오래된 시작 노드(오래된 노드 a)DOM 앞 에 추가 한 다음 에 새로운 시작 노드 번호 에 1 을 추가 하면 새로운 노드 f 가 처리 되 었 음 을 나타 내 고 현재 새로운 시작 노드 c 를 처리 하고 있 습 니 다.

    3.새로운 노드 의 시작 과 끝 노드 는 구 노드 의 시작 과 끝 노드 와 서로 다 르 지만 구 노드 에서 새로운 시작 노드(노드 c)와 같은 노드 를 찾 습 니 다.
    그래서 구 노드 c 의 DOM 을 구 시작 노드(구 노드 a)DOM 앞 에 추가 하고 구 노드 c 를 비 운 다음 에 새로운 시작 노드 번호 에 1 을 추가 하면 새 노드 c 가 처리 되 었 고 현재 새로운 시작 노드 e 를 처리 하고 있 음 을 나타 낸다.

    4.새 시작 노드(새 노드 e)는 옛 끝 노드(옛 노드 e)와 같다.구 노드 e 의 DOM 내용 을 업데이트 하고 구 노드 e 의 DOM 을 구 시작 노드(구 노드 a)DOM 앞으로 이동 합 니 다.구 끝 노드 번 호 는 1 을 줄 이 고 새로운 시작 노드 는 1 을 더 하면 신 구 노드 e 가 처리 되 었 음 을 나타 냅 니 다.현재 처리 하고 있 는 것 은 새로운 시작 노드 g 와 구 끝 노드 d 입 니 다.

    5.새 엔 딩 노드(새 노드 d)는 구 엔 딩 노드(구 노드 d)와 같다.오래된 노드 d 의 DOM 내용 만 업데이트 합 니 다.새로운 엔 딩 노드 의 번호 가 1 을 줄 이 고 낡은 엔 딩 노드 의 번호 가 1 을 줄 이 는 것 은 신 구 노드 d 가 처리 되 었 음 을 나타 낸다.현재 처리 하고 있 는 것 은 새로운 엔 딩 노드 g 와 낡은 엔 딩 노드 c 이다.낡은 노드 c 가 비어 있 기 때문에 낡은 끝 노드 는 b 이다.

    6.새로운 노드 의 시작 과 끝 노드 는 구 노드 의 시작 과 끝 노드 가 서로 다 르 고 구 노드 에서 새로운 시작 노드(새 노드 g)와 같은 노드 를 찾 지 못 했다.
    그래서 노드 g 의 DOM 을 만 들 고 오래된 시작 노드(오래된 노드 a)DOM 앞 에 추가 한 다음 에 새로운 시작 노드 번호 에 1 을 추가 하면 새로운 노드 g 가 처리 되 었 음 을 나타 내 고 현재 새로운 시작 노드 d 를 처리 하고 있 습 니 다.

    7.새로운 시작 과 끝 노드 의 번호 가 겹 치기 때문에 새 노드 가 처리 되 었 고 처리 되 지 않 은 오래된 노드 가 존재 하면 처리 되 지 않 은 오래된 노드 DOM 을 제거 합 니 다.

    8.끝,최종 DOM.

    vue 페이지 업데이트 패 치 의 실현 예제 에 관 한 이 글 은 여기까지 소개 되 었 습 니 다.더 많은 vue 업데이트 패 치 내용 은 우리 의 이전 글 을 검색 하거나 아래 의 관련 글 을 계속 조회 하 시기 바 랍 니 다.앞으로 많은 응원 바 랍 니 다!

    좋은 웹페이지 즐겨찾기