Vue 가상 Dom 과 diff 알고리즘 을 알 아 보 는 글

머리말
Vue 와 React 를 사용 한 파트너 는 가상 Dom 과 diff 알고리즘 에 익숙 하고 중요 한 역할 을 할 것 입 니 다.소 편 은 Vue 와 접촉 하 는 것 이 비교적 많 기 때문에 React 는 얕 은 학문 일 뿐 이 므 로 본 편 은 주로 Vue 에 대해 소 개 를 하고 한 걸음 한 걸음 알 아 보 겠 습 니 다.
가상 DOM
가상 DOM 이란 무엇 입 니까?
가상 DOM(가상 DOM)   Dom),즉 우리 가 흔히 말 하 는 가상 노드 는 JS 대상 으로 실제 DOM 중의 노드 를 모 의 하 는 것 이다.이 대상 은 실제 DOM 의 구조 와 속성 을 포함 하고 가상 DOM 과 실제 DOM 의 차 이 를 비교 하여 부분 적 인 렌 더 링 을 하여 성능 을 최적화 하 는 목적 을 달성 하 는 데 사용 된다.
실제 요소 노드:

<div id="wrap">
 <p class="title">Hello world!</p>
</div>
VNode:

{
 tag:'div',
 attrs:{
 id:'wrap'
 },
 children:[
 {
  tag:'p',
  text:'Hello world!',
  attrs:{
  class:'title',
  }
 }
 ]
}
가상 DOM 을 왜 사용 합 니까?
가상 DOM 에 대해 간단히 알 고 나 면 Vue 와 React 프레임 워 크 에 왜 사용 되 느 냐 고 물 어 보 는 파트너 가 있 지 않 습 니까?좋 은 문제!그럼 동료 들 의 의문 을 해결 해 보 자.
처음에 우 리 는 JS/JQuery 를 사용 할 때 DOM 을 대량으로 조작 할 수 밖 에 없 었 고 DOM 의 변 화 는 환류 나 재 그림 을 일 으 켜 페이지 렌 더 링 성능 을 떨 어 뜨 렸 다.그러면 어떻게 하면 DOM 에 대한 조작 을 줄 일 수 있 을까요?이때 가상 DOM 응용 프로그램 이 생 겨 났 기 때문에 가상 DOM 이 나타 난 주요 목적 은 잦 은 DOM 조작 을 줄 이기 위해 환류 재 그림 으로 인 한 성능 문 제 를 일 으 키 는 것 입 니 다!
가상 DOM 의 역할 은 무엇 입 니까?
  • 호환성 이 좋다.Vnode 는 본질 적 으로 JS 대상 이기 때문에 Node 든 브 라 우 저 환경 이 든 모두 조작 할 수 있 습 니 다.
  • 돔 에 대한 조작 을 줄 였 다.페이지 의 데이터 와 상태 변 화 는 모두 Vnode 를 통 해 비교 되 고 비교 가 끝 난 후에 DOM 을 업데이트 해 야 합 니 다.잦 은 조작 이 필요 하지 않 고 페이지 성능 을 향상 시 킵 니 다.
  • 가상 DOM 과 실제 DOM 의 차이 점 은?
    그렇다면 가상 DOM 과 실제 DOM 의 차 이 는 무엇 일 까?총 결 은 대략 다음 과 같다.
  • 가상 DOM 은 환류 와 재 그림 을 하지 않 습 니 다.
  • 실제 DOM 이 빈번 한 조작 을 할 때 발생 하 는 환류 재 그림 은 성능 이 매우 낮다.
  • 가상 DOM 이 빈번하게 수정 한 다음 에 한꺼번에 차 이 를 비교 하고 실제 DOM 을 수정 한 다음 에 순서대로 환류 재 그림 을 그 려 서 실제 DOM 에서 여러 번 환류 재 그림 으로 인 한 성능 손실 을 감소 했다.
  • 가상 DOM 은 대면 적의 재 구성 과 레이아웃 을 효과적으로 낮 출 수 있 습 니 다.실제 DOM 과 비교 하여 차이 점 을 업데이트 하기 때문에 부분 만 과장 합 니 다.
  • 총 손실=실제 DOM 첨삭 수정+(다 중 노드)환류/다시 그리 기;    //실제 DOM 을 사용 한 손실 을 계산 하 다.
    총 손실=가상 DOM 첨삭 수정+(diff 대비)실제 DOM 차별 화 첨삭 수정+(적은 노드)환류/다시 그리 기;   //가상 DOM 사용 손실 계산
    빈번 한 조작 을 둘 러 싼 실제 DOM 이 회 류 를 일 으 켜 페이지 의 성능 손실 을 초래 한 것 을 발견 할 수 있다.그러나 프레임 워 크 도 반드시 가상 DOM 을 사용 해 야 하 는 것 은 아니다.잦 은 조작 이 대면 적의 DOM 조작 을 일 으 킬 수 있 는 지 를 보 는 것 이 관건 이다.
    과연 가상 DOM 은 어떤 방식 으로 페이지 의 잦 은 DOM 조작 을 줄 였 을 까?DOM Diff 알고리즘 을 알 아야 합 니 다.
    DIFF 알고리즘
    데이터 가 변 할 때 vue 는 어떻게 보 기 를 업데이트 합 니까?사실은 간단 합 니 다.처음에 실제 DOM 에 따라 가상 DOM 을 생 성 합 니 다.가상 DOM 의 특정한 노드 의 데이터 가 바 뀌 면 새로운 Vnode 를 생 성 한 다음 에 VNode 와 oldVnode 를 비교 하여 서로 다른 부분 을 실제 DOM 에 수정 한 다음 에 oldVnode 의 값 을 Vnode 로 만 듭 니 다.
    diff 과정 은 patch 함 수 를 호출 하여 새로운 노드 를 비교 하면 서 실제 DOM 에 패 치(patch)를 하 는 것 입 니 다.
    vue 소스 코드 를 대조 하여 해석 하고 핵심 코드 를 붙 이 는 것 은 간단명료 하 게 설명 하 는 데 목적 을 둡 니 다.그렇지 않 으 면 작은 편집 이 자신 이 보기 만 해도 머리 가 큽 니 다 O(∩∩)O
    patch
    그럼 패 치 는 어떻게 패 치 를 하 는 거 예요?
    
    //patch   oldVnode:    vnode:   
    function patch (oldVnode, vnode) {
     ...
     if (sameVnode(oldVnode, vnode)) {
     patchVnode(oldVnode, vnode) //           ,       patchVnode      
     } else {
     /* -----            ----- */
     const oEl = oldVnode.el //   oldVnode         
     let parentEle = api.parentNode(oEl) //    
     createEle(vnode) //   Vnode     
     if (parentEle !== null) {
      api.insertBefore(parentEle, vnode.el, api.nextSibling(oEl)) //           
      api.removeChild(parentEle, oldVnode.el) //           
      oldVnode = null
     }
     }
     ...
     return vnode
    }
    
    //            
    function sameVnode (a, b) {
     return (
     a.key === b.key && // key 
     a.tag === b.tag && //    
     a.isComment === b.isComment && //        
     //       data,data        ,  onclick , style
     isDef(a.data) === isDef(b.data) && 
     sameInputType(a, b) //     <input>   ,type    
     )
    }
    
    위 에서 알 수 있 듯 이 patch 함 수 는 신 구 노드 가 같은 노드 인지 아 닌 지 를 판단 하 는 것 입 니 다.
  • 같은 노드 라면 patchVnode 를 실행 하여 하위 노드 를 비교 합 니 다.
  • 같은 노드 가 아니라면 새 노드 는 오래된 노드 를 직접 교체 합 니 다.
  • 만약 같은 노드 가 아니라면,그것들의 하위 노드 가 같 으 면 어떻게 하지?OMG,명심 하 세 요:diff 는 같은 층 의 비교 이 고 등급 을 뛰 어 넘 는 비교 가 존재 하지 않 습 니 다!간단하게 말하자면 React 에서 도 마찬가지 로 같은 층 의 노드 를 비교 할 뿐이다.
    patchVnode
    patchVnode 방법 에 도 착 했 으 니 신 구 노드 가 같은 노드 라 는 것 을 설명 한다 면 이 방법 은 어떤 처 리 를 했 습 니까?
    
    function patchVnode (oldVnode, vnode) {
     const el = vnode.el = oldVnode.el  //       DOM
     let i, oldCh = oldVnode.children, ch = vnode.children 
     if (oldVnode === vnode) return  //        ,    
     if (oldVnode.text !== null && vnode.text !== null && oldVnode.text !== vnode.text) {
     //                ,                    
     api.setTextContent(el, vnode.text) 
     }else {
     updateEle(el, vnode, oldVnode)
     if (oldCh && ch && oldCh !== ch) {
      //           ,  updateChildren     [       ,      ]
      updateChildren(el, oldCh, ch)
     }else if (ch){
      //                  ,                 
      createEle(vnode)
     }else if (oldCh){
      //                  ,           
      api.removeChildren(el)
     }
     }
    }
    
    만약 에 두 노드 가 다 르 면 새로운 노드 로 오래된 노드 를 교체 합 니 다.
    두 노드 가 같다 면,
  • 신 구 노드 와 같이 바로 돌아간다.
  • 오래된 노드 는 하위 노드 가 있 고 새로운 노드 는 없다.오래된 노드 의 하위 노드 를 삭제 하 는 것 이다.
  • 오래된 노드 는 하위 노드 가 없고 새로운 노드 는 하위 노드 가 있다.새로운 노드 의 하위 노드 는 직접 append 에서 오래된 노드 로 간다.
  • 모두 텍스트 노드 만 있 습 니 다.새로운 노드 의 텍스트 노드 로 오래된 텍스트 노드 를 직접 교체 합 니 다.
  • 모두 하위 노드 가 있 습 니 다.updateChildren
  • 가장 복잡 한 상황 은 바로 신 구 노드 에 모두 하위 노드 가 있다 는 것 이다.그러면 updateChildren 은 이 문 제 를 어떻게 처리 하 는 지,이 방법 도 diff 알고리즘 의 핵심 이다.다음은 우리 가 알 아 보 자!
    updateChildren
    코드 가 너무 많아 서 여기 서 먼저 개술 을 하 겠 습 니 다.updateChildren 방법의 핵심:
  • 새로운 노드 의 하위 노드 추출:새로운 노드 ch 와 오래된 노드 oldCh;
  • ch 와 oldCh 는 각각 StartIdx(지향 헤드)와 EndIdx(지향 꼬리)변 수 를 설정 합 니 다.두 가지 비교(sameNode 방법 에 따라)는 네 가지 방식 으로 비교 합 니 다.4 가지 방식 이 모두 일치 하지 않 으 면 key 를 설정 하면 key 를 통 해 비교 합 니 다.비교 과정 에서 startIdx+,endIdx--는 StartIdx>EndIdx 가 ch 나 oldCh 가 적어도 한 개 이상 옮 겨 다 니 고 있다 는 것 을 나타 내 면 비 교 를 끝 냅 니 다.
  • 다음은 그림 을 결합 하여 이해 합 니 다.

    첫 번 째 단계:
    
    oldStartIdx = A , oldEndIdx = C;
    newStartIdx = A , newEndIdx = D;
    
    이때 oldstartIdx 는 new StarIdx 와 일치 하기 때문에 dom 의 A 노드 를 첫 번 째 위치 에 놓 습 니 다.이때 A 는 첫 번 째 위치 에 있 기 때문에 처리 하지 않 습 니 다.이때 실제 DOM 순서:A  B  C;
    두 번 째 단계:
    
    oldStartIdx = B , oldEndIdx = C;
    newStartIdx = C , oldEndIdx = D;
    

    이때 oldendIdx 는 new StartIdx 와 일치 하여 원래 의 C 노드 를 A 뒤로 이동 합 니 다.이때 실제 DOM 순서:A   C   B;
    세 번 째 단계:
    
    oldStartIdx = C , oldEndIdx = C;
    newStartIdx = B , newEndIdx = D;
    oldStartIdx++,oldEndIdx--;
    oldStartIdx > oldEndIdx
    
    이때 옮 겨 다 니 기 가 끝 났 습 니 다.oldCh 는 이미 옮 겨 다 니 고 있 습 니 다.그러면 나머지 ch 노드 를 자신의 index 에 따라 실제 DOM 에 삽입 하면 됩 니 다.이때 실제 DOM 순서:A  C  B  D;
    그래서 매 칭 과정 에서 판단 이 끝 나 는 데 두 가지 조건 이 있 습 니 다.
  • oldstartIdx>oldEndIdx 는 oldCh 가 먼저 옮 겨 다 니 는 것 을 나타 내 고 ch 에 남 은 노드 가 있 으 면 해당 index 에 따라 실제 DOM 에 추가 합 니 다.
  • new StartIdx>new EndIdx 는 ch 가 먼저 옮 겨 다 니 는 것 을 표시 합 니 다.그러면 실제 DOM 에서 나머지 노드 를 삭제 해 야 합 니 다.
  • 다음 그림 의 인 스 턴 스 를 보 세 요.새 노드 를 옮 겨 다 니 며 나머지 노드 를 삭제 하 는 것 입 니 다.

    마지막 으로,이 하위 노드 sameVnode 이후 조건 을 만족 시 키 면 patchVnode 를 계속 실행 하고,oldVnode 와 Vnode 의 모든 하위 노드 가 완 성 될 때 까지,모든 패 치 를 다 쳐 서 보기 로 업데이트 합 니 다.
    총결산
    마지막 으로,한 장의 그림 으로 전체 Diff 과정 을 기억 하고,당신 이 얻 을 수 있 기 를 바 랍 니 다!

    달걀
    React 는 단순히 기 초 를 배 웠 을 뿐 이 므 로 대비 하여 개술 해 보 겠 습 니 다.
    1.React 렌 더 링 메커니즘:React 는 가상 DOM 을 사용 합 니 다.속성 과 상태 가 변 할 때마다 render 함 수 는 서로 다른 요소 트 리 를 되 돌려 주 고 돌아 오 는 요소 트 리 와 지난번 렌 더 링 트 리 의 차 이 를 비교 하여 차이 점 을 업데이트 합 니 다.마지막 으로 실제 DOM 으로 렌 더 링 합 니 다.
    2.diff 는 항상 같은 층 으로 비교 합 니 다.만약 에 노드 유형 이 다 르 면 새로운 것 으로 오래된 것 을 교체 합 니 다.만약 에 노드 의 유형 이 같 으 면 그들의 서브 노드 를 비교 하고 순서대로 유추 한다.보통 요소 에 연 결 된 key 값 은 노드 를 비교 하 는 데 사용 되 기 때문에 그 유일 성 을 확보 해 야 합 니 다.보통 배열 아래 표 시 를 key 값 으로 하지 않 습 니 다.배열 요소 가 변화 할 때 index 가 바 뀌 기 때 문 입 니 다.
    3.렌 더 링 메커니즘 의 전체 과정 은 업데이트 작업 을 포함 하고 가상 DOM 을 실제 DOM 으로 전환 하기 때문에 전체 렌 더 링 과정 은 Reconciliation 이다.이 과정의 핵심 은 주로 diff 알고리즘 이 고 생명주기 shouldComponentUpdate 함 수 를 이용한다.
    Vue 가상 Dom 과 diff 알고리즘 을 알 고 있 는 이 글 은 여기까지 소개 되 었 습 니 다.Vue 가상 Dom 과 diff 알고리즘 에 관 한 더 많은 내용 은 저희 의 이전 글 을 검색 하거나 아래 의 관련 글 을 계속 찾 아 보 세 요.앞으로 많은 응원 부 탁 드 리 겠 습 니 다!

    좋은 웹페이지 즐겨찾기