원리 깊이 분석 Vue 의 응답 식 업데이트 가 React 보다 빠 릅 니 다.

머리말
우 리 는 Vue 가 응답 식 속성 업데이트 에 대해 수집 에 의존 하 는 현재 구성 요소 만 정확하게 업데이트 하고 하위 구성 요 소 를 재 귀적 으로 업데이트 하지 않 는 다 는 것 을 알 고 있 습 니 다.이것 도 성능 이 강 한 이유 중 하나 입 니 다.
예시
예 를 들 어 이러한 구성 요소:

<template>
  <div>
   {{ msg }}
   <ChildComponent />
  </div>
</template>
this.msg='Hello,Changed~'를 실행 할 때 구성 요소 의 업 데 이 트 를 실행 하고 보기 의 렌 더 링 을 다시 시작 합 니 다.
그러나이 구성 요 소 는 사실 다시 과장 되 지 않 습 니 다.이것 은 Vue 가 의도 한 것 입 니 다.
예전 에 저 는 구성 요소 가 나무 라 고 생각 했 기 때문에 이 나 무 를 옮 겨 다 니 며 재 귀적 으로 업데이트 하 는 것 이 당연 하 다 고 생각 했 습 니 다.이 편 은 소스 코드 의 측면 에서 Vue 가 어떻게 정확하게 업 데 이 트 했 는 지 분석 해 드 리 겠 습 니 다.
React 업데이트 입도
한편,React 는 비슷 한 장면 에서 위 에서 아래로 재 귀적 으로 업 데 이 트 를 한 것 이다.즉,React 에 ChildComponent 에 10 층 의 하위 요소 가 있다 면 모든 단계 가 재 귀적 으로 render(수 동 최적화 되 지 않 은 상황 에서)되 는 것 은 성능 상의 재난 이다.(그래서 React 는 Fiber 를 만 들 었 고 비동기 렌 더 링 을 만 들 었 으 며 사실은 본질 적 으로 자신 이 망 친 성능 을 보완 하 는 것 이다).
그들 은 수집 에 의존 하 는 이 시스템 을 사용 할 수 있 습 니까?아 닙 니 다.그들 은 Immutable 의 디자인 사상 을 따 르 기 때문에 원래 의 대상 에서 속성 을 수정 하지 않 습 니 다.그러면 Object.defineProperty 나 Proxy 의 응답 식 의존 수집 체 제 를 바탕 으로 손 을 쓸 수 없습니다.
또한,응답 식 수집 의존 이 없 기 때문에 React 는 모든 하위 구성 요 소 를 재 렌 더 링 할 수 밖 에 없습니다.
Vue 의 업데이트 입도
그럼 Vue 라 는 정확 한 업 데 이 트 는 어떻게 하 는 건 가요?사실 모든 구성 요 소 는 렌 더 링 워 치 를 가지 고 있 습 니 다.현재 구성 요소 의 보기 업 데 이 트 를 관리 하지만 ChildComponent 의 업 데 이 트 를 관리 하지 않 습 니 다.
구체 적 으로 소스 코드 에서 어떻게 실현 되 었 습 니까?
패 치  이 과정 에서 구성 요소 가 ChildComponent 로 업 데 이 트 될 때 patchVnode 로 갑 니 다.이 방법 은 대체적으로 어떤 일 을 했 습 니까?
patchVnode
vnode 의 prepatch 갈 고 리 를 실행 합 니 다.
구성 요소 vnode 만 prepatch 라 는 라 이 프 사이클 이 있 습 니 다.
업데이트 Child Component 방법 으로 갑 니 다.이 child 는 구체 적 으로 무엇 을 말 합 니까?

 prepatch (oldVnode: MountedComponentVNode, vnode: MountedComponentVNode) {
  const options = vnode.componentOptions
  //      child  ChildComponent    vm   ,          this
  const child = vnode.componentInstance = oldVnode.componentInstance
  updateChildComponent(
   child,
   options.propsData, // updated props
   options.listeners, // updated listeners
   vnode, // new parent vnode
   options.children // new children
  )
 },
사실 들 어 오 는 매개 변 수 를 보면 대충 알 아 맞 힐 수 있 습 니 다.바로 했 습 니 다.
  • props 업데이트(추가 설명)
  • 귀속 이벤트 업데이트
  • slot 에 대한 업데이트(후속 상세 설명)
  • 하위 노드 가 있 으 면 하위 노드 에 diff 를 진행 합 니 다.
    예 를 들 어 이런 장면:
    
    <ul>
     <li>1</li>
     <li>2</li>
     <li>3</li>
    <ul>
    
    얼 의 세 개의 li 서브 노드 vnode 에 대해 diff 알고리즘 을 이용 하여 업데이트 하려 면 이 편 은 생략 합 니 다.
    그리고 여기까지 패 치 Vnode 는 끝 났 습 니 다.일반적인 생각 처럼 하위 구성 요소 트 리 를 재 귀적 으로 업데이트 하지 않 았 습 니 다.
    이 는 Vue 의 구성 요소 업데이트 가 구성 요소 자체 에 정확 하 다 는 것 을 의미한다.
    하위 구성 요소 라면?
    목록 이 이 렇 습 니 다.
    
    <ul>
     <component>1</component>
     <component>2</component>
     <component>3</component>
    <ul>
    
    그러면 diff 과정 에서 component 에서 설명 한 props,listeners 등 속성 만 업데이트 하고 구성 요소 내부 에 깊이 들 어가 업데이트 하지 않 습 니 다.
    메모:구성 요소 내부 에 깊이 들 어가 업데이트 하지 않 습 니 다!(중점 을 두 는 것 도 본 고 에서 말 한 입도 갱신 의 관건 이다)
    props 의 업 데 이 트 는 어떻게 렌 더 링 을 촉발 합 니까?
    그러면 학생 들 이 물 어 볼 수도 있 습 니 다.하위 구성 요 소 를 재 귀적 으로 업데이트 하지 않 으 면 msg 라 는 응답 요 소 를 props 를 통 해 Child Component 에 전달 하면 어떻게 업데이트 합 니까?
    우선,구성 요소 가 props 를 초기 화 할 때 initprops 방법 으로 갑 니 다.
    
    const props = vm._props = {}
    
     for (const key in propsOptions) {
      //        props       
      const value = validateProp(key, propsOptions, propsData, vm)
      // props             
      defineReactive(props, key, value)
    }
    
    
    지금까지 실현 되 었 습 니 다.props 필드 변경 납치.즉,응답 식 데이터 가 되 었 습 니 다.그 다음 에 우 리 는 와 유사 합 니 다.props.msg='Changed'동작 을 할 때(물론 우 리 는 이렇게 하지 않 습 니 다.Vue 내부 에서 할 것 입 니 다)보기 업 데 이 트 를 촉발 합 니 다.
    사실 msg 는 하위 구성 요소 에 전 달 될 때 하위 구성 요소 인 스 턴 스 에 저 장 됩 니 다props 에 서 는 응답 식 속성 으로 정의 되 었 으 며,하위 구성 요소 의 템 플 릿 에 서 는 msg 에 대한 접근 이 로 대 리 됩 니 다.props.msg 가 올 라 갔 기 때문에 자 연 스 럽 게 의존 도 를 정확하게 수집 할 수 있 습 니 다.ChildComponent 가 템 플 릿 에서 도 이 속성 을 읽 으 면 됩 니 다.
    부모 구성 요소 가 다시 렌 더 링 될 때 하위 구성 요소 의 props 를 다시 계산 합 니 다.구체 적 으로 는 updateChild Component 에 있 습 니 다.
    
     // update props
     if (propsData && vm.$options.props) {
      toggleObserving(false)
      //   props     _props
      const props = vm._props
      const propKeys = vm.$options._propKeys || []
      for (let i = 0; i < propKeys.length; i++) {
       const key = propKeys[i]
       const propOptions: any = vm.$options.props // wtf flow?
       //      ,      _props.msg      。
       props[key] = validateProp(key, propOptions, propsData, vm)
      }
      toggleObserving(true)
      // keep a copy of raw propsData
      vm.$options.propsData = propsData
     }
    그러면 위 에 설명 되 어 있 는 그 코드 로 인해 msg 의 변 화 는props 의 응답 식 능력 도 하위 구성 요 소 를 다시 렌 더 링 시 켰 습 니 다.지금까지 msg 를 사용 한 구성 요소 만 다시 렌 더 링 되 었 습 니 다.
    홈 페이지 api 문서 에서 말 한 바 와 같이:
    vm.$forceUpdate:Vue 인 스 턴 스 를 다시 렌 더 링 하도록 합 니 다.모든 하위 구성 요소 가 아 닌 인 스 턴 스 자체 와 슬롯 내용 을 삽입 하 는 하위 구성 요소 에 만 영향 을 줍 니 다.
    ―― vm-forceUpdate 문서
    우 리 는 작은 지식 을 알 아야 합 니 다.vm.$forceUpdate 는 본질 적 으로 렌 더 링 워 치 의 재 실행 을 촉발 하 는 것 입 니 다.응답 식 속성 을 수정 하여 업 데 이 트 를 촉발 하 는 원 리 는 똑 같 습 니 다.vm. 만 호출 해 주 었 을 뿐 입 니 다.watcher.update()(편리 한 api 만 제공 하고 디자인 모델 에서 외관 모델 이 라 고 합 니 다)
    slot 는 어떻게 업 데 이 트 됩 니까?
    여기 서도 세부 사항 을 언급 했 습 니 다.즉,슬롯 내용 을 삽입 하 는 하위 구성 요소 입 니 다.
    예 를 들 면
    부모 구성 요소 parent-comp 가 있다 고 가정 합 니 다.
    
    <div>
     <slot-comp>
       <span>{{ msg }}</span>
     </slot-comp>
    </div>
    
    하위 구성 요소 slot-comp:
    
    <div>
      <slot></slot>
    </div>
    
    구성 요소 에 slot 업데이트 가 포함 되 어 있 으 며,비교적 특수 한 장면 에 속 합 니 다.
    이 msg 속성 은 수집 에 의존 할 때 parent-comp 의 렌 더 링 watcher 를 수집 합 니 다.(왜 그런 지 에 대해 서 는 그것 이 있 는 렌 더 링 문맥 을 보면 알 수 있다.)
    그럼 우 리 는 msg 가 이때 업데이트 되 었 다 고 상상 합 니 다.
    
    <div>
     <slot-comp>
       <span>{{ msg }}</span>
     </slot-comp>
    </div>
    
    이 구성 요 소 는 업데이트 할 때 하위 구성 요소 slot-comp 를 만 났 습 니 다.Vue 의 정확 한 업데이트 전략 에 따 르 면 하위 구성 요 소 는 다시 렌 더 링 되 지 않 습 니 다.
    그러나 소스 코드 내부 에서 slot-comp 의 prepatch 라 는 hook 를 실행 할 때 updateChild Component 논 리 를 실행 합 니 다.이 함수 내부 에서 slot 요소 가 있 음 을 발견 할 수 있 습 니 다.
    
     prepatch (oldVnode: MountedComponentVNode, vnode: MountedComponentVNode) {
      const options = vnode.componentOptions
      //      child   slot-comp     vm   ,          this
      const child = vnode.componentInstance = oldVnode.componentInstance
      updateChildComponent(
       child,
       options.propsData, // updated props
       options.listeners, // updated listeners
       vnode, // new parent vnode
       options.children // new children
      )
     },
    
    updateChildComponent 내부 에서
    
     const hasChildren = !!(
      //       slot   
      renderChildren ||        // has new static slots
      vm.$options._renderChildren || // has old static slots
      parentVnode.data.scopedSlots || // has new scoped slots
      vm.$scopedSlots !== emptyObject // has old scoped slots
     )
    그리고 다음 판단 을 내 려 보도 록 하 겠 습 니 다.
    
     if (hasChildren) {
      vm.$slots = resolveSlots(renderChildren, parentVnode.context)
      vm.$forceUpdate()
     }
    
    slot-comp 구성 요소 vm 인 스 턴 스 의$forceUpdate 를 호출 했 습 니 다.렌 더 링 워 치 는 slot-comp 의 렌 더 링 워 치 에 속 합 니 다.
    결론 적 으로 이번 msg 의 업 데 이 트 는 parent-comp 의 렌 더 링 뿐만 아니 라 slot 를 가 진 하위 구성 요소 slot-comp 의 렌 더 링 도 촉발 시 켰 습 니 다.
    이것 도 단지 두 겹 의 렌 더 링 을 촉발 시 켰 을 뿐이다.만약 slot-comp 내부 에 또 다른 구성 요소 인 slot-child 가 렌 더 링 되 었 다 면,이 때 는 귀속 업 데 이 트 를 하지 않 을 것 이다.(slot-child 구성 요소 만 있 으 면 slot 가 없습니다.
    React 의 재 귀적 업데이트 보다 훨씬 낫 지 않 을까요?
    작은 이 슈 하나 드릴 게 요.
    누군가가 Vue 2.4.2 버 전에 하 나 를 제시 했다issue.아래 장면 에서 bug 가 나타 날 것 이다.
    
    let Child = {
     name: "child",
     template:
      '<div><span>{{ localMsg }}</span><button @click="change">click</button></div>',
     data: function() {
      return {
       localMsg: this.msg
      };
     },
     props: {
      msg: String
     },
     methods: {
      change() {
       this.$emit("update:msg", "world");
      }
     }
    };
    
    new Vue({
     el: "#app",
     template: '<child :msg.sync="msg"><child>',
     beforeUpdate() {
      alert("update twice");
     },
     data() {
      return {
       msg: "hello"
      };
     },
     components: {
      Child
     }
    });
    
    
    구체 적 인 표현 은 click 단 추 를 누 르 면 alert 에서 두 번 의 update twice 가 나 옵 니 다.이것 은 하위 구성 요소 가 data 라 는 함 수 를 실행 하여 구성 요소 의 데 이 터 를 초기 화 할 때 Dep.target(즉,렌 더 링 watcher)을 잘못 수집 하기 때 문 입 니 다.
    데이터 초기 화 시 기 는 beforeCreated->created 사이 이기 때문에 하위 구성 요소 의 렌 더 링 단계 에 들 어가 지 않 았 기 때문에 Dep.target 은 부모 구성 요소 의 렌 더 링 watcher 입 니 다.
    이 로 인해 중복 수집 의존 을 초래 하고 같은 업 데 이 트 를 반복 적 으로 촉발 합 니 다.
    어떻게 해 결 했 지?간단 합 니 다.data 함 수 를 실행 한 후에 Dep.target 을 먼저 null 로 설정 하면 됩 니 다.finally 에서 복원 하면 응답 식 데 이 터 를 의존 할 수 없습니다.
    
    export function getData (data: Function, vm: Component): any {
     const prevTarget = Dep.target
    + Dep.target = null
     try {
      return data.call(vm, vm)
     } catch (e) {
      handleError(e, vm, `data()`)
      return {}
    + } finally {
    +  Dep.target = prevTarget
     }
    }
    
    후기
    만약 에 Dep.target,렌 더 링 watcher 등 개념 에 대해 잘 이해 하지 못 한다 면 제 가 쓴 가장 간단 한 Vue 응답 식 글 을 볼 수 있 습 니 다.읽 기 를 환영 합 니 다.
    Vue 의 data,coptute,watch 소스 를 배우 기 위해 가장 간단 한 응답 시스템 을 실현 합 니 다.
    본문 도 제Github 블 로그 창고에 저장 되 어 있 습 니 다.구독 과 스타 를 환영 합 니 다.
    원리 에 대한 심도 있 는 해석 Vue 의 응답 식 업데이트 가 React 보다 빠 른 글 을 소개 합 니 다.더 많은 Vue 에 대한 응답 식 업데이트 가 React 보다 빠 른 내용 은 이전 글 을 검색 하거나 아래 의 관련 글 을 계속 찾 아 보 세 요.앞으로 많은 응원 부 탁 드 리 겠 습 니 다!

    좋은 웹페이지 즐겨찾기