Vue 3의 반응성 탐색: ref 및 watchEffect

이것은 Frontend 프레임워크의 내부를 탐구하는 시리즈입니다. 그 목적은 이러한 프레임워크가 내부에서 작동하는 방식을 밝히는 것입니다. 초보자나 코드 아래에서 무슨 일이 일어나고 있는지 자주 알 필요가 없는 사람에게는 적합하지 않을 수 있습니다.

그리고 시리즈의 두 번째 첫 번째 부분에서는 Vue 3의 반응성이 refwatchEffect 와 어떻게 작동하는지 살펴보겠습니다.

Vue 3의 반응성



이 문서의 범위 내에서 지금은 refwatchEffect만 살펴보겠습니다. 다른 반응성 함수는 시리즈의 다음 부분에서 살펴볼 것입니다.

우리는 여기에서 반응성에 대해 많이 이야기하고 있습니다. 그렇다면 실제로 반응성은 무엇입니까? 사실 새로운 패러다임은 아닙니다. 일반적인 예는 Excel 스프레드시트입니다.



위의 예에서 B1 셀은 = A1 + A2 로 정의됩니다. A1 또는 A2를 업데이트하면 B1도 사후 업데이트됩니다.

그러나 JavaScript에서는 변수가 그런 식으로 작동하지 않습니다.

let A1 = 1
let A2 = 4
let B1 = A1 + A2

console.log(B1) // 5

A1 = 3

console.log(B1) // still 5


우리의 목적이 A1 또는 A2가 변경될 때 A1과 A2의 합계를 기록하는 것이라면 Vue 3에서 다음과 같이 작성할 수 있습니다.

const A1 = ref(1)
const A2 = ref(4)

watchEffect(() => {
  console.log('B1 =', A1.value + A2.value)
})


하지만 어떻게 작동합니까? watchEffect는 A1 또는 A2가 언제 변경되는지 어떻게 알 수 있습니까? 이 질문에 답하기 위해 처음부터 ref를 빌드해 보겠습니다. getter 및 setter가 있는 속성이 1개value인 개체로 시작합니다.

export function ref(value) {
  return new RefImpl(value)
}

class RefImpl {
  private _value

  constructor(value) {
    this._value = value
  }

  get value() {
    return this._value
  }

  set value(newVal) {
    this._value = newVal
  }
}


아직 아무 일도 일어나지 않습니다. 여기서 사용할 마술은 value 개체의 ref 속성을 읽을 때(getter 메서드 호출) 자동으로 호출자를 ref 개체의 구독자로 추가하는 것입니다. 또한 이 구독자를 효과(부작용의 줄임말)라고 합니다. ref 개체는 이제 효과의 종속성이 됩니다.

export function ref(value) {
  return new RefImpl(value)
}

class RefImpl {
  private _value
  public dep = undefined // Ironically, in the Vue's codebase, the subscribers/effects of a ref object seem to also be called its dependencies.

  constructor(value) {
    this._value = value
  }

  get value() {
    trackRefValue(this)

    return this._value
  }

  set value(newVal) {
    this._value = newVal;
  }
}

export function trackRefValue(ref) {
  if (!activeEffect) return;

  if (!ref.dep) ref.dep = new Set() // We must use Set here to avoid duplication

  ref.dep.add(activeEffect) // Add the active (currently running) effect as one of the ref object's subscribers
}


이제 2가지 문제가 남았습니다.
  • activeEffect의 값은 어디에서 왔습니까? 즉, 현재 어떤 효과가 실행되고 있는지 어떻게 알 수 있습니까?
  • ref 속성이 변경될 때 모든 value 개체의 구독자/종속성을 트리거/알려야 합니다.

  • 두 번째 문제는 매우 간단하기 때문에 먼저 다루겠습니다.

    export function ref(value) {
      return new RefImpl(value)
    }
    
    class RefImpl {
      private _value
      public dep = undefined // Ironically, in the Vue's codebase, the subscribers/effects of a ref object seem to also be called its dependencies.
    
      constructor(value) {
        this._value = value
      }
    
      get value() {
        trackRefValue(this)
    
        return this._value
      }
    
      set value(newVal) {
        this._value = newVal
    
        triggerRefValue(this)
      }
    }
    
    export function trackRefValue(ref) {
      if (!activeEffect) return;
    
      if (!ref.dep) ref.dep = new Set() // We must use Set here to avoid duplication
    
      ref.dep.add(activeEffect) // Add the active (currently running) effect as one of the ref object's subscribers
    }
    
    export function triggerRefValue(ref) {
      if (!ref.dep) return;
    
      for (const effect of ref.dep) {
        effect() // run the effect
      }
    }
    


    이제 남은 가장 중요한 질문은 activeEffect의 값은 어디에서 오는가? watchEffect의 원시 구현을 살펴보겠습니다.

    export let activeEffect = undefined
    
    export function watchEffect(effectHandler) {
      const effect = () => {
        activeEffect = effect
    
        effectHandler()
        // The above function call will read the value property of any ref object inside it
        // and trigger the getter method of the value property,
        // which in turn adds this effect as one of the subscribers of that ref object.
    
        activeEffect = undefined
      }
    
      effect() // watchEffect triggers the effect (handler) immediately
    }
    

    watchEffect는 항상 즉시 실행되기 때문에 처음 실행될 때 항상 내부에 있는 value 개체의 ref 속성의 getter 메서드를 트리거하고 효과를 이러한 ref 개체의 구독자로 등록합니다. 이것이 watchEffect 에 대한 종속성을 명시적으로 지정할 필요가 없는 이유입니다.

    다음은 refwatchEffect의 작업 코드 예제입니다.



    지금은 refwatchEffect 의 대략적인 작동 버전이 있습니다. 실패할 수 있는 경우를 너무 많이 생략하고 일괄 처리가 없으며 여기에서 제어 흐름이 약간 엉망이기 때문에 거의 사용할 수 없습니다. 그러나 바라건대 뒤에서 무슨 일이 일어나고 있는지에 대한 지나치게 단순화된 예로서 그 목적에 부합합니다.

    결론



    시리즈의 이 부분에서는 Vue 3의 refwatchEffect의 요지를 활용했습니다. 다음 부분에서는 refwatchEffect의 지나치게 단순화된 버전을 계속 개선하여 실제 구현과 더 가깝게 일치하도록 하고 reactive , computedwatch 에 대해서도 살펴보겠습니다.

    어떻게든 유용하게 사용하시길 바랍니다. 제안이나 조언이 있으시면 주저하지 말고 의견 섹션에서 저에게 연락하십시오.

    좋은 웹페이지 즐겨찾기