FrameWorks/Vue

Vue의 ref 파헤치기

ABCD 2024. 6. 12.

ref( )

  • 단일 값을 갖을 수 있다.
  • 안타깝게도 Proxy는 객체에 대해서만 동작하므로 단일 값을 객체에 집어 넣어야 한다.
  • 통상적인 Vue의 ref( )의 구현은 reactive( )를 활용하지 않고, 객체 접근자를 통해 구현되어 있다.

reactive( )를 활용한 구현

function ref(initialValue){
  return reactive({value : initalValue});
}

객체 접근자를 활용한 구현

JavaScript Object 접근자

  • JavaScript Object Accessors는 JavaScript computed 속성이라고도 한다.
    • 주의!! Vue computed 속성과 혼동하지 말것!!
  • (나의 생각) Java에서 객체를 생성하는 방식으로 getter, setter와 유사하며, json형태로 표기한 것
let user = {
  firstName: 'Gregg',
  lastName: 'Pollack',

  get fullName() {
    return '${this.firstName} ${this.lastName}'
  },

  set fullName(value) {
    [this.firstName, this.lastName] = value.split(' ')
  },
}

console.log(`Name is ${user.fullName}`);
user.fullName = 'Adam Jahr';
console.log(`Name is ${user.fullName}`);

구현 코드

const targetMap = new WeakMap() // targetMap stores the effects that each object should re-run when it's updated
let activeEffect = null // The active effect running

function track(target, key) {
  if (activeEffect) {
    // <------ Check to see if we have an activeEffect
    // We need to make sure this effect is being tracked.
    let depsMap = targetMap.get(target) // Get the current depsMap for this target
    if (!depsMap) {
      // There is no map.
      targetMap.set(target, (depsMap = new Map())) // Create one
    }
    let dep = depsMap.get(key) // Get the current dependencies (effects) that need to be run when this is set
    if (!dep) {
      // There is no dependencies (effects)
      depsMap.set(key, (dep = new Set())) // Create a new Set
    }
    dep.add(activeEffect) // Add effect to dependency map
  }
}

function trigger(target, key) {
  const depsMap = targetMap.get(target) // Does this object have any properties that have dependencies (effects)
  if (!depsMap) {
    return
  }
  let dep = depsMap.get(key) // If there are dependencies (effects) associated with this
  if (dep) {
    dep.forEach((effect) => {
      // run them all
      effect()
    })
  }
}

function reactive(target) {
  const handler = {
    get(target, key, receiver) {
      let result = Reflect.get(target, key, receiver)
      track(target, key) // If this reactive property (target) is GET inside then track the effect to rerun on SET
      return result
    },
    set(target, key, value, receiver) {
      let oldValue = target[key]
      let result = Reflect.set(target, key, value, receiver)
      if (result && oldValue != value) {
        trigger(target, key) // If this reactive property (target) has effects to rerun on SET, trigger them.
      }
      return result
    },
  }
  return new Proxy(target, handler)
}

function ref(raw) {
  const r = {
    get value() {
      track(r, 'value')
      return raw
    },
    set value(newVal) {
      raw = newVal
      trigger(r, 'value')
    },
  }
  return r
}

// function ref(intialValue) {
//   return reactive({ value: initialValue })
// }

function effect(eff) {
  activeEffect = eff
  activeEffect()
  activeEffect = null
}

let product = reactive({ price: 5, quantity: 2 })
let salePrice = ref(0)
let total = 0

effect(() => {
  salePrice.value = product.price * 0.9
})

effect(() => {
  total = salePrice.value * product.quantity
})

console.log(
  `Before updated quantity total (should be 9) = ${total} salePrice (should be 4.5) = ${salePrice.value}`
)
product.quantity = 3
console.log(
  `After updated quantity total (should be 13.5) = ${total} salePrice (should be 4.5) = ${salePrice.value}`
)
product.price = 10
console.log(
  `After updated price total (should be 27) = ${total} salePrice (should be 9) = ${salePrice.value}`
)
728x90
반응형

'FrameWorks > Vue' 카테고리의 다른 글

Vue의 watchEffect 와 watch 파헤치기  (0) 2024.06.12
Vue의 ModelValue 파헤치기  (0) 2024.06.12
Vue의 nextTick() 파헤치기  (0) 2024.06.12
Vue의 BaseHandler 파헤치기  (0) 2024.06.12
reactive( )  (0) 2023.07.11

댓글

💲 추천 글