[Vue.js] Vue 3 Reactivity (#1)
하수도키
·2020. 7. 22. 18:25
728x90
반응형
SMALL
[Vue.js] Vue 3 Reactivity (#1)
개요
-
이 문서는 www.vuemastery.com 에
Vue 3 Reactivity
를 보고 정리했습니다. - Vue3가 드디어 릴리즈가 되었다. 아직 베타인것 같지만...
- Vue3 Reactivity System(반응형)에 대해 알아보자.
-
이걸 알면 좋은 점은??
- Vue 내부 디자인 패턴 이해
- Vue 디버깅 스킬 향상
- Vue3 modularzied 반응형 라이브러리 사용
- Vue3 소스 코드에 기여 가능
- 어마어마한 혜택들이 있다.
반응형(Reactivity) 이해하기
- Vue 반응형은 정말 매직과 같다. 아래 코드로 어떻게 작동하는지 살펴보자.
<template>
<div id="app">
<div>Price: ${{ product.price }}</div>
<div>Total: ${{ product.price * product.quantity }}</div>
<div>Taxes: ${{ totalPriceWithText }}</div>
</div>
</template>
<script>
export default {
name: "App",
data: () => ({
product: {
price: 5.00,
quantity: 2,
}
}),
computed: {
totalPriceWithText() {
return this.product.price * this.product.quantity * 1.03
}
}
};
</script>
-
Vue 반응형은
price
가 변하면 3가지 일을 한다.price
값 웹페이지에 갱신- 표현식
price * quantity
갱신 totalPriceWithTax
함수 다시 호출 후 갱신
-
Vue 반응형은
price
가 변경될때 무엇이 갱신해야 하는지 어떻게 알까? -
이건 일반적인
JavaScript
작동하는 방법이 아니다.
let product = { price: 5, quantity: 2 }
let total = product.price * product.quantity // 10
product.price = 20
console.log(`total is ${total}`) // 10
total
의 값을 40을 기대했지만 10이 나온다.JavaScript
는 절차적이고 반응형이 아니므로price
의 변경을 바로 반영하여total
의 값을 변경하지 않는다.
반응형 간단한 코드로 이해해보기
- 위에 설명한 코드를
JavaScript
에서 반응형으로 작동하기 위해서는 아래와 같은 순서로 작업이 필요하다.total = product.price * product.quantity
이 부분 코드를 저장하고price
가 변경되면 다시 저장한 코드를 호출한다.
let product = { price: 5, quantity: 2 };
let total = 0;
let effect = () => { total = product.price * product.quantity }
track() // effect 함수를 저장하는 역할 나중에 다시 설명
effect()
-
track()
을 정의하기 위해서는effect
들을 저장할 장소가 필요하다. -
dep
이라는 종속성있는Set
을 만든다.- Set 객체는 자료형에 관계 없이 원시 값과 객체 참조 모두 유일한 값을 저장할 수 있습니다.
-
종속성이라고 부르는 이유는 옵저버 패턴을 참고했다. 옵저버 패턴에서 종속성은
subscribers
(여기선 effect)를 가지고 있다. -
객체 상태가 변경될때
subscribers
에게 알려준다. - 위 내용을 정리한 코드이다.
let dep = new Set()
function track() {
dep.add(effect)
}
- 이제 실행할 함수를 작성해보자.
function trigger() {
dep.forEach(effect => effect())
}
product.price = 20
console.log(total) // => 10
trigger()
console.log(total) // => 40
- 위에 내용들을 정리한 코드
let product = { price: 5, quantity: 2 }
let total = 0
let dep = new Set()
function track() {
dep.add(effect)
}
function trigger() {
dep.forEach(effect => effect())
}
let effect = () => {
total = product.price * product.quantity
}
track()
effect()
product.price = 20
console.log(total) // => 10
trigger()
console.log(total) // => 40
다수의 속성들은 어떻게 반응형으로 만들까?
- 문제가 발생했다. 바로 다수의 속성들일 경우 어떻게 처리할까?
- 현재까지 객체의
price
만 생각했는데quantity
로 반응형으로 처리해야 될 경우는? depsMap
를 만들어 처리한다.depsMap
은Map
자료구조를 가진다.- Map 객체는 키-값 쌍을 저장하며 각 쌍의 삽입 순서도 기억하는 콜렉션입니다. 아무 값(객체와 원시 값)이라도 키와 값으로 사용할 수 있습니다.
depsMap
에 새로운 효과(effect
)를 속성 이름(key
)에 추가하는 법을 살펴보자.
const depsMap = new Map()
function track(key) {
// Make sure this effect is being tracked.
let dep = depsMap.get(key) // key로 depsMap에 저장되어 있는 값(effect가 있는 저장소)을 가져오자
if (!dep) {
// dep이 없다면 새로 저장소를 만들자.
depsMap.set(key, (dep = new Set())) // Create a new Set
}
dep.add(effect) // dep에 effect를 추가
}
}
function trigger(key) {
let dep = depsMap.get(key) // key를 이용해 dep을 가져오자 다시 한번 말하지만 effect가 있는 저장소
if (dep) { // dep이 있을때만 실행
dep.forEach(effect => {
// run them all
effect()
})
}
}
let product = { price: 5, quantity: 2 }
let total = 0
let effect = () => {
total = product.price * product.quantity
}
track('quantity')
effect()
console.log(total) // --> 10
product.quantity = 3
trigger('quantity')
console.log(total) // --> 40
다수의 객체들일때는?
- 현재까지 하나의 객체만 살펴보았지만 이번 경우는 다수의 객체들일때 문제점을 파악하고 해결해보자.
WeakMap
을 사용한다.- WeakMap 객체는 키가 약하게 참조되는 키/값 쌍의 컬렉션입니다. 키는 객체여야만 하나 값은 임의 값이 될 수 있습니다.
let product = { price: 5, quantity: 2 }
const targetMap = new WeakMap()
targetMap.set(product, "example code to test")
console.log(targetMap.get(product)) // ---> "example code to test"
- 객체 대상을 고려하는
targetMap
을WeakMap
으로 생성한다.
track
,trigger
실행할때 어떤 타겟 객체인지 알아야 한다. 그래서target
,key
둘다 보내서 확인한다.
const targetMap = new WeakMap()
function track(target, key) {
let depsMap = targetMap.get(target) // depsMap(객체) 구하기
if (!depsMap) {
targetMap.set(target, (depsMap = new Map()))
}
let dep = depsMap.get(key) // 객체의 속성값을 이용해 effect가 담긴 저장소 가져오기
if (!dep) {
depsMap.set(key, (dep = new Set()))
}
dep.add(effect) // effect를 저장소에 추가하기
}
function trigger(target, key) {
const depsMap = targetMap.get(target) // targetMap에 effect 실행할 객체 가져오기
if (!depsMap) {
return
}
let dep = depsMap.get(key) // 실행한 객체 속성이 있는 경우
if (dep) {
dep.forEach(effect => {
// 모두 실행
effect()
})
}
}
let product = { price: 5, quantity: 2 }
let total = 0
let effect = () => {
total = product.price * product.quantity
}
track(product, 'quantity')
effect()
console.log(total) // --> 10
product.quantity = 3
trigger(product, 'quantity')
console.log(total) // --> 15
- 여러 객체에 대한 종속성을 추적하는 방법을 알아봤다.
- 다음에는
track
,trigger
호출할때 ES6 proxy를 사용하는 법을 알아보자.
결론
- Vue2 일때도 잘 몰랐던 내용인데 이번 계기로 자세히 알아보자.
- 아무 생각없이 잘 변경되는구나...라고 생각했었는데 이런 내부 로직을 알게되니 다양하게 사용이 가능할 것 같다.
728x90
반응형
LIST
'개발일기 > Vue.js' 카테고리의 다른 글
[Vue.js] Vue 테스트 코드 작성하기(Jest) (0) | 2020.08.21 |
---|---|
[Vue.js] 토큰 기반 인증(#2. 인증을 위한 프로젝트 구조 살피기) (3) | 2020.07.27 |
[Vue.js] 토큰 기반 인증(#1. 인증소개) (2) | 2020.07.24 |
[Vue.js] highcharts 설치 및 세팅 (0) | 2019.09.03 |
[Vue.js] Vue CLI 설치 및 세팅 (0) | 2019.09.02 |