본문 바로가기

FrontEnd

vue3의 반응성(reactivity) 원리 간단 정리

반응형

출처 : https://vuejs.org/guide/extras/reactivity-in-depth.html

 

Reactivity in Depth | Vue.js

 

vuejs.org

아래와 같은 코드가 실행된다 가정하자

import { ref, watchEffect } from 'vue'

const A0 = ref(0)
const A1 = ref(1)
const A2 = ref()

watchEffect(() => {
  // tracks A0 and A1
  A2.value = A0.value + A1.value
})

// triggers the effect
A0.value = 2

1. 이펙트(ex: watchEffect,computed 내부 함수) 함수가 전역 변수에 할당되며 파라미터로 받은 업데이트 함수를 실행함

  • 초기에 A2 값을 만들기 위함
// 전역 변수
let activeEffect;

function watchEffect(update) {
  const effect = () => {
    activeEffect = effect
    update()
    activeEffect = null
  }
  effect()
}

2. 업데이트 함수 내에서 reactive한 객체에 접근하여 프록시 트랩 get 호출

  • return target[key]를 통해 값을 리턴함
    • 그 전에 track 함수를 실행
function reactive(obj) {
  return new Proxy(obj, {
    get(target, key) {
      track(target, key)
      return target[key]
    },
    set(target, key, value) {
      target[key] = value
      trigger(target, key)
    }
  })
}

function ref(value) {
  const refObject = {
    get value() {
      track(refObject, 'value')
      return value
    },
    set value(newValue) {
      value = newValue
      trigger(refObject, 'value')
    }
  }
  return refObject
}

3. 프록시 트랩 내의 track함수는 1에서 전역 변수에 할당된 이펙트를 해당 속성의 구독자 목록에 추가함

  • 예를 들어 A2= A1+A0이면, update 함수는 ()=>A2.value=A1.value+A0.value임
    • 해당 update 함수를 내부에서 호출하는 effect 함수는 각각 A1속성, A0속성의 구독자 목록에 추가됨.
  • getSubscribersForProperty는 WeakMap<target, Map<key, Set<effect>>>에서 객체와 키를 통해 이펙트 목록를 가져오는 함수라 생각 가능
// This will be set right before an effect is about
// to be run. We'll deal with this later.
let activeEffect

function track(target, key) {
  if (activeEffect) {
    const effects = getSubscribersForProperty(target, key)
    effects.add(activeEffect)
  }
}

4. 반응형 객체의 특정 속성값이 변경되면, 해당 속성값에 대한 구독자 함수를 전부 호출해줌

  • 2의 set 프록시 트랩
  • 예를 들어 A2= A1+A0이면
    • A1이 변경되면 effect 호출하여 A2 갱신
    • A0이 변경되면 effect호출하여 A2 갱신
function trigger(target, key) {
  const effects = getSubscribersForProperty(target, key)
  effects.forEach((effect) => effect())
}

5. 해당 객체에 바인딩 된 뷰가 업데이트됨

  • 뷰의 patch, update 라이프사이클

비고 

프록시를 통한 반응성을 얻으려면, 프록시 객체를 통해 객체 프로퍼티에 직접 접근 가능해야함.

  • 로컬 변수에 할당하거나 구조분해 할 경우, primitive 값의 경우는 의존성 추적이 끊길 수 있음. (새 이름표에 새 값 할당)
  • 당연히 원본과 동등 비교 === 불가
  •  

 

반응형