[Front-end] - Vue data reactivity
Created: July 6, 2022 2:15 PM Date: July 6, 2022 2:15 PM Tags: Vue.js, observer, proxy
Vue 2 data reactivity
In Vue 2, data reactivity is achieved by traversing the data, and making use ofย Object.definedProperty()
ย to convert its properties to getter/setter. It collects data dependencies via custom getter, and monitors data change and subscribe events in a custom setter.
defineReactive ์ฝ๋๋ถ์
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter() {
const value = getter ? getter.call(obj) : val
if (Dep.target) {
dep.depend()
if (childOb) {
childOb.dep.depend()
if (Array.isArray(value)) {
dependArray(value)
}
}
}
return value
},
set: function reactiveSetter(newVal) {
const value = getter ? getter.call(obj) : val
/* eslint-disable no-self-compare */
if (newVal === value || (newVal !== newVal && value !== value)) {
return
}
/* eslint-enable no-self-compare */
if (process.env.NODE_ENV !== 'production' && customSetter) {
customSetter()
}
// #7981: for accessor properties without setter
if (getter && !setter) return
if (setter) {
setter.call(obj, newVal)
} else {
val = newVal
}
childOb = !shallow && observe(newVal)
dep.notify()
},
})
์ ํจ์๋ ์ด๊ธฐํ defineReactive
๋ ๋ ๋ฐ์ดํฐ ๊ฐ์ฒด์ ๋ชจ๋ ์์ฑ์ ๋ํด ํธ์ถ๋๋ค.
Observer getter๊ฐ ์ข ์์ฑ์ ์์งํ๋๋ก ์ ์๋ ๊ฒ์ ๋ณผ ์ ์์ผ๋ฉฐ ์ค์ ์ ๋ฐ์ดํฐ ๋ณ๊ฒฝ์ ๋ชจ๋ํฐ๋งํ๊ณ ๋ณ๊ฒฝ์ด ๊ฐ์ง๋๋ฉด ์๋ฆผ์ ๋ณด๋ธ๋ค.
์ ๋ฉ์ปค๋์ฆ์์ ๋๊ฐ์ง ๋ฌธ์ ๊ฐ ๋ฐ์ํจ
- ์์ฑ์ ์ญ์ or ์ถ๊ฐ๋ฅผ ๊ฐ์งํ ์ ์์
- ๋ฐ์์ฑ์ ์ฑ์ด ์ด๊ธฐํ๋ ๋๋ง ์ ์ฉ๋๋ค. ๋ฐํ์์ ์ ์์ฑ์ ์ถ๊ฐํ๋ฉด ์ ์์ฑ์ ๋ฐ์ํ์ง ์๋๋ค. ์ฆ, ์์ฑ ๊ฐ์ ๋ณ๊ฒฝํด๋ ๋ฐ์์ ์ธ ๋ถ์์ฉ์ด ๋ฐ์ํ์ง ์๋๋ค. Vue2๋
.set
์ ๊ฐ๋ฐ์๊ฐ ์๋์ผ๋ก ์์ฑ์ ๋ฐ์ํ์ผ๋ก ์ถ๊ฐํ ์ ์๋๋ก ํ๋ ํด๊ฒฐ๋ฐฉ๋ฒ์ ์ ๊ณตํ๋ค.
- ๋ฐ์์ฑ์ ์ฑ์ด ์ด๊ธฐํ๋ ๋๋ง ์ ์ฉ๋๋ค. ๋ฐํ์์ ์ ์์ฑ์ ์ถ๊ฐํ๋ฉด ์ ์์ฑ์ ๋ฐ์ํ์ง ์๋๋ค. ์ฆ, ์์ฑ ๊ฐ์ ๋ณ๊ฒฝํด๋ ๋ฐ์์ ์ธ ๋ถ์์ฉ์ด ๋ฐ์ํ์ง ์๋๋ค. Vue2๋
- ์ฑ๋ฅ
- ๋๊ท๋ชจ/ ์ค์ฒฉ ๋ฐ์ดํฐ ์ ์ ๊ฒฝ์ฐ vue2๊ฐ ๋ชจ๋ ์์ฑ์ ๋ฐ์ดํฐ ํก๋จ์ getter/setter ์์ฑ์ด ํ์ํ๊ธฐ ๋๋ฌธ์ ์ฑ๋ฅ์ ๋ถ์ ์ ์ธ ์ํฅ์ ๋ฏธ์น ์ ์๋ค.
vue3
es6์ ๋์ ๋ Proxy๋ฅผ ์ฌ์ฉํ์ฌ ๊ธฐ์กด์ ์ํ๊ฐ์ฒด์ getter/setter๋ฅผ ์ด๊ฒ์ผ๋ก ๋์ฒดํ์ฌ ๋ฐ์์ฑ์ ๊ตฌํ๊ฐ๋ฅ.
ํ๋ก์๋ ์๋ก์ด ์์ฑ ์ถ๊ฐ๋ฅผ ๊ฐ์งํ ์ ์์
MDN Proxy
[Proxy - JavaScript | MDN](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Proxy) |
createReactiveObject ๋ถ์
function createReactiveObject(target: Target, isReadonly: boolean, baseHandlers: ProxyHandler<any>, collectionHandlers: ProxyHandler<any>, proxyMap: WeakMap<Target, any>) {
if (!isObject(target)) {
if (__DEV__) {
console.warn(`value cannot be made reactive: ${String(target)}`)
}
return target
}
// target is already a Proxy, return it.
// exception: calling readonly() on a reactive object
if (target[ReactiveFlags.RAW] && !(isReadonly && target[ReactiveFlags.IS_REACTIVE])) {
return target
}
// target already has corresponding Proxy
const existingProxy = proxyMap.get(target)
if (existingProxy) {
return existingProxy
}
// only a whitelist of value types can be observed.
const targetType = getTargetType(target)
if (targetType === TargetType.INVALID) {
return target
}
const proxy = new Proxy(target, targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers)
proxyMap.set(target, proxy)
return proxy
}
- ์ ์ฝ๋๋ ๊ฐ์ฒด๋ง ์ฒ๋ฆฌํ๊ธฐ๋ฅผ ์ํ๋ฏ๋ก ๊ธฐ๋ณธ ์ ํ์ ๋ฐ์ดํฐ๊ฐ ์ง์ ๋ฐํํฉ๋๋ค. vue3๋ ๋์ ref๋ฅผ ์ฌ์ฉํ์ฌ ๊ธฐ๋ณธ ์ ํ์ ์ฒ๋ฆฌ. ๋ด๋ถ์ ์ผ๋ก ๋ฐ์ ํ๋ก์ ๊ฐ์ฒด๋ฅผ ์ฌ์ฉ
- ๊ฐ์ฒด์ ์ด๋ฏธ ํด๋น ํ๋ก์๊ฐ ์์ ๊ฒฝ์ฐ ์บ์๋ ํ๋ก์๋ฅผ ์ง์ ๋ฐํ
- ๋์ ๊ฐ์ฒด ์ ํ์ ์ ์ถํ๋ฉด vue3๋ Array, Object, Map, Set, WeakMap, WeakSet์ ๋ํด์๋ง ํ๋ก์๋ฅผ ์์ฑ. ๋์ ์ ํ ์ธ๋ถ์ ๊ฐ์ฒด๋ INVALID๋ก ์ค์ ๋๊ณ ๋ฐํ
- ์ ํ๋ก์ ๊ฐ์ฒด๋ฅผ ๋ง๋ค๊ณ ๋ฐํํ๊ธฐ ์ ์ proxyMap์บ์์ ์ ์ฅ
Proxy๋ Vue2 ๋ฐ์์ฑ ๋ฌธ์ ๋ฅผ ์ด๋ป๊ฒ ํด๊ฒฐํ ๊น?
proxy ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ๋ฉด ๊ฐ์ฒด์ ์ก์ธ์คํ๊ฑฐ๋ ๊ฐ์ฒด๋ฅผ ์์ ํ๊ธฐ ์ํ ๋ชจ๋ ํธ์ถ์ด ์ฐจ๋จ๋๋ค. ์ฌ์ฉ์ ์ ์ ์์ ์ getter / setter์์ ์ ์๋๋ค.
const hadKey = isArray(target) && isIntegerKey(key) ? Number(key) < target.length : hasOwn(target, key)
const result = Reflect.set(target, key, value, receiver)
// don't trigger if target is something up in the prototype chain of original
if (target === toRaw(receiver)) {
if (!hadKey) {
trigger(target, TriggerOpTypes.ADD, key, value)
} else if (hasChanged(value, oldValue)) {
trigger(target, TriggerOpTypes.SET, key, value, oldValue)
}
}
์์ฑ์ด ์กด์ฌํ์ง ์๋ ๊ฒฝ์ฐ(hadKey
false์ธ ๊ฒฝ์ฐ) ํธ๋ฆฌ๊ฑฐ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ ์์์ฑ์ด ์ถ๊ฐ๋์์์ ์ข
์์ฑ์ ์๋ฆฐ๋ค. ์ด๊ฒ์ Vue2์์ ์ถ๊ฐ ์์ฑ์ ๊ฐ์งํ ์ ์๋ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ค. ๋ง์ฐฌ๊ฐ์ง๋ก deleteProperty
ํธ๋ค๋ฌ ์์
์ Vue2์์ ์ญ์ ์์ฑ์ ๊ฐ์ง ํ ์ ์๋ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ค.
Vue3์์ ๋ฐ์์ฑ API์ ํ๋ก์ ๊ตฌํ์ ์ฌ์ฉํ๋ฉด ๋ฐ์ดํฐ์ ๋ชจ๋ ์์ฑ์ ํ์ํ ํ์๊ฐ ์๋ค. ์ด๋ ๋๊ท๋ชจ ๋ฐ์ดํฐ ์ธํธ๋ฅผ ์ฒ๋ฆฌํ ๋ ์๋นํ ์ฑ๋ฅ ํฅ์์ ๊ฐ์ ธ์จ๋ค.
๊ฒฐ๋ก
ํ๋ก์๋ ๊ฐ๋ ฅํ ๋ฉํํ๋ก๊ทธ๋๋ฐ ๊ธฐ๋ฅ์ด๋ค. Vue3์์ Proxy๋ฅผ ์ ์ฉํ๋ ๊ฒ์ ํ๋ฅญํ ์๋ฃจ์ ์ ๋๋ค. Vue2์ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ๋ฟ ์๋๋ผ ์ถ๊ฐ ํ์ฅ ๊ฐ๋ฅ์ฑ๋ ์ด์ด์ค๋ค.
์ฐธ๊ณ