Models๋ proxied Javascript Object์ด๋ค. ๋ชจ๋ธ์ ๋ณ๊ฒฝํ๋ฉด, ๋ทฐ๊ฐ ์ ๋ฐ์ดํธ ๋๋ค. ์ด๊ฒ ์ด๋ป๊ฒ ์ด๋ฃจ์ด์ง๋์ง๋ฅผ ๋ทฐ์ ๋ฐ์ํ ์์คํ ์ ๋ด๋ถ ๊ตฌ์กฐ๋ฅผ ํ์ ํ๋ฉด์ ์ดํดํด๋ณด์.
- ์ฐธ๊ณ : vue-next์ effect.ts ์ฝ๋
value๊ฐ ๋ฐ๋ ๋, sum์ด ๋ฐ๋๊ธฐ ์ํด์๋ sum์ ํจ์ ์์ sum์ ๋ฃ์ด์ผ ํ๋ค.
const updateSum = () => {
sum = val1 + val2
}
Vue์๊ฒ ์ด๋ป๊ฒ ์ด ํจ์๋ฅผ ์ค๋ช ํ ์ ์์๊น?
Vue๋ effect
๋ฅผ ์ฌ์ฉํ์ฌ ํ์ฌ ๋์ํ๊ณ ์๋ ํจ์๋ฅผ ์ถ์ ํ๋ค. effect
๋ ํจ์๋ฅผ ๊ฐ์ธ๋ wrapper์ธ๋ฐ, ์ด๋ ํจ์๊ฐ ํธ์ถ๋๊ธฐ ์ ์ tracking์ ์์ํ๋ค. ๋ทฐ๋ ์ด๋ ํ effect๊ฐ ๋์ํ๊ณ ์๋์ง๋ฅผ ์ด๋ ์์ ์์๋ ์ง ์ ์ ์๊ณ , ํ์ํ ๋ ๋ค์ ์คํ์ํฌ ์ ์๋ค.
์ด๋ฅผ ์ดํดํ๊ธฐ ์ํด, ๋ทฐ๊ฐ ํ๋ ์ญํ ๊ณผ ๋น์ทํ ๊ฒ์ ํ๋ด๋ด๋ณด์.
createEffect(() => {
sum = val1 + val2
})
sum์ ๊ฐ์ธ๋ createEffect
๋ฅผ ๋ง๋ ๋ค.
- createEffect๋ sum์ด ์ธ์ ๋์ํ๊ณ ์๋์ง๋ฅผ ์ถ์ ํด์ผ ํ๋ค.
-
ํ์ฌ ๋์ํ๊ณ ์๋ effect๋ค์ ์ ์ฅํ๋ runningEffects๋ผ๋ ๋ฐฐ์ด์ ๋๋ค.
-
effect๊ฐ ํธ์ถ๋๋ฉด, ํจ์ ํธ์ถ ์ง์ ์ ์๊ธฐ ์์ ์ runningEffects ๋ฐฐ์ด์ ์ถ๊ฐํ๋ค.
=> ์ด๋ ํ effect๊ฐ ํ์ฌ ์คํ๋๊ณ ์๋์ง๋ runningEffects ๋ฐฐ์ด์ ํ์ธํ๋ฉด ๋๋ค.
-
// Maintain a stack of running effects
const runningEffects = []
const createEffect = fn => {
// Wrap the passed fn in an effect function
const effect = () => {
runningEffects.push(effect)
fn()
runningEffects.pop()
}
// Automatically run the effect immediately
effect()
}
Effects๋ ๋ค๋ฅธ ๊ธฐ๋ฅ๋ค์ starting point๊ฐ ๋๋ค. ์๋ฅผ ๋ค์ด, component rendering๊ณผ computed property๋ ๋ด๋ถ์ ์ผ๋ก effect๋ฅผ ์ฌ์ฉํ๋ค. ์ด๋ค ๋ฐ์ดํ๊ฐ ๋ณ๊ฒฝ๋์์ ๋, ๋ฐ์ํ๋ ๊ฒ์ด ์๋ค๋ฉด, ๊ทธ๊ฒ์ด effect๋ก ๊ฐ์ธ์ ธ์๊ตฌ๋๋ฅผ ์๊ฐํ๋ฉด ๋๋ค.
ํ์ฌ ์คํํ๊ณ ์๋ ํจ์๋ effect๋ก ๊ฐ์ธ์ ์ ์ ์์์ ์์์ ์ดํด๋ณด์๋ค. ์ฌ๊ธฐ์๋ 1. ๋ทฐ๊ฐ ์ด๋ป๊ฒ effect์ Data ๊ฐ์ ์์กด๊ด๊ณ๋ฅผ ์์๋ด๋์ง 2. ์ด๋ป๊ฒ Reactivity๋ฅผ ๋ง๋ค์ด๋ด๋์ง๋ฅผ ์ดํด๋ณผ ๊ฒ์ด๋ค.
local ๋ณ์๋ฅผ ์ฌํ ๋นํ๋ ๊ฒ์ ์ถ์ ํ ์ ์๋ค. ๊ทธ๋ฌํ ๋ฉ์ปค๋์ฆ์ด ์๋ฐ์คํฌ๋ฆฝํธ์๋ ์๊ธฐ ๋๋ฌธ์ด๋ค. ์ถ์ ํ ์ ์๋ ๊ฒ์ ๊ฐ์ฒด์ ํ๋กํผํฐ๊ฐ ๋ณ๊ฒฝ๋์์ ๊ฒฝ์ฐ์ด๋ค.
์ปดํฌ๋ํธ์ data
ํจ์๊ฐ plain js object๋ฅผ ๋ฐํํ๋ฉด, ๋ทฐ๋ ๊ทธ object์ get, set handler๋ฅผ Proxy
๋ก ๊ฐ์ผ๋ค.
์ฐธ๊ณ : Proxy๋ ํด๋น ๊ฐ์ฒด๋ฅผ interceptํ์ฌ ์ํ๋ operation์ ์ํํ๋๋ก ์กฐ์ํ ์ ์๋ ๊ฐ์ฒด์ด๋ค. Reflect๋ this binding์ด Proxy๋ก ๋๋๋ก ํด์ค๋ค.
๋น ๋ฅด๊ฒ ์์ ์ง๋ฌธ์ ๋ํ ๋ต์ ๋ด๋ฆฌ์๋ฉด, 1. ๋ทฐ๊ฐ ์ด๋ป๊ฒ effect์ Data ๊ฐ์ ์์กด๊ด๊ณ๋ฅผ ์์๋ด๋๊ฐ
- ํ๋ก์๋ก get์ interceptํ์ฌ ํ๋กํผํฐ์ effect๊ฐ์ ์์กด๊ด๊ณ๋ฅผ ํ์ ํ ์ ์๋ค.
- ์ด๋ป๊ฒ Reactivity๋ฅผ ๋ง๋ค์ด๋ด๋๊ฐ
- ํ๋ก์๋ก set์ interceptํ์ฌ ํ๋กํผํฐ๊ฐ ๋ณ๊ฒฝ๋์์ ๋ effect๋ฅผ ๋ค์ ์คํ์ํฌ ์ ์๋ค.
const dinner = {
meal: 'tacos'
}
const handler = {
get(target, property, receiver) {
track(target, property)
return Reflect.get(...arguments)
},
set(target, property, value, receiver) {
trigger(target, property)
return Reflect.set(...arguments)
}
}
const proxy = new Proxy(dinner, handler)
console.log(proxy.meal)
// tacos
const handler = {
get(target, property, receiver) {
track(target, property)
return Reflect.get(...arguments)
},
...
}
Proxy๋ก Reactivity๋ฅผ ๊ตฌํํ๋ ์ฒซ ๋ฒ์งธ ๋ฐฉ๋ฒ์ Property๊ฐ ์ฝํ์ง ๋, ์ด๋ ํ effect๊ฐ ํด๋น ํ๋กํผํฐ์ ์ ๊ทผํ์๋์ง๋ฅผ ์์๋ด๋ ๊ฒ์ด๋ค.
์ด๋ฅผ ์ถ์ ํ๊ธฐ ์ํด์๋ ํ์ฌ ์ด ํ๋กํผํฐ์ ์ ๊ทผํ ๊ฒ์ด ๋ฌด์์ธ์ง๋ฅผ ํ์ธํด์ผ ํ๋ค.
์ด๋ฅผ ์ํด์๋ getter๋ฅผ interceptํด์ผ ํ๋ค.
ํ๋ก์์ get handler๋ track์ด๋ผ๋ ํจ์์ argument๋ก target๊ณผ property๋ฅผ ๋ฃ์ด ํธ์ถํ๋ค.
track ํจ์๋ ํ์ฌ ๋์ํ๊ณ ์๋ effect
๊ฐ ๋ฌด์์ธ์ง ํ์ธํ๊ณ , target
, property
์ ๊ฐ์ด ๊ธฐ๋กํ๋ค.
์ด๋ ๊ฒํ์ฌ ๋ทฐ๋ ํด๋น effect๊ฐ target์ property์ ์์กดํ๊ณ ์์์ ์ ์ ์๋ค.
2. data๊ฐ ๋ณ๊ฒฝ๋์์ ๋, ๊ทธ ๊ฐ์ ์์กดํ๋ effect ๋ค์ ์คํ์ํค๊ธฐ : intercept set
const handler = {
set(target, property, value, receiver) {
trigger(target, property)
return Reflect.set(...arguments)
},
...
}
์ด๋ ํ ๋ฐ์ดํฐ๊ฐ ๋ณ๊ฒฝ๋๋ฉด, setter๊ฐ ํธ์ถ๋๋ค.
Proxy์ ํธ๋ค๋ฌ๋ set์ ๊ฐ๋ก์ฑ์, ํ์ฌ target์ property์ ์์กดํ๊ณ ์๋ effects๋ค์ ๋ค์ ์คํ์ํจ๋ค.
์ ๋ฆฌ
- dependency-tracking์ handler.get์์ ์ฒ๋ฆฌ
- change-notification์ handler.set์์ ์ฒ๋ฆฌํ ์ ์๊ฒ ๋๋ค.
Vue๋ ๋ด๋ถ์ ์ผ๋ก reactiveํ๊ฒ ๋ง๋ ๋ชจ๋ ๊ฐ์ฒด๋ฅผ ์ถ์ ํ๊ณ ์๋ค. ๋ฐ๋ผ์ ํญ์ ๊ฐ์ ๊ฐ์ฒด์ ๋ํด ๊ฐ์ ํ๋ก์๋ฅผ ๋ฐํํ๋ค.
๋ง์ฝ reactive proxy์ ์๋ ์ค์ฒฉ๋ ํ๋กํผํฐ์ ์ ๊ทผํ๋ค๋ฉด, ๊ทธ ์ค์ฒฉ๋ ๊ฐ์ฒด ๋ํ ํ๋ก์๋ก ๋ณํ๋๋ค.
const handler = {
get(target, property, receiver) {
track(target, property)
const value = Reflect.get(...arguments)
if (isObject(value)) {
// Wrap the nested object in its own reactive proxy
return reactive(value)
} else {
return value
}
}
// ...
}
Proxy ๊ฐ์ฒด์ ์ค๋ฆฌ์ง๋ ๊ฐ์ฒด๋ ===
๋ก ํ๋จํ์ ๋ ๊ฐ์ง ์๋ค. .includes()
๋ .indexOf()
์ ๊ฐ์ด strict equality comparison์ ํ๋ ์ฐ์ฐ์๋ ์ด๋ฌํ ํน์ฑ์ด ์ํฅ์ ๋ผ์น๊ฒ ๋๋ค.
const obj = {}
const wrapped = new Proxy(obj, handlers)
console.log(obj === wrapped) // false
๊ฐ์ฅ ์ข์ ๊ฒ์ ์ค๋ฆฌ์ง๋ ๊ฐ์ฒด๋ฅผ ์ฐธ์กฐ(Reference)ํ๋ ๊ฒ์ ๋์ง ์๋ ๊ฒ์ด๋ค. ์ค๋ก์ง reactiveํ ๊ฐ์ฒด๋ก๋ง ๋์ํ๋๋ก ํด์ผ ํ๋ค. ์ด๋ ๊ฒ ํด์ผ equality comparison๊ณผ reactivity๊ฐ ์ํ๋๋๋ก ๋์ํ๊ฒ ๋๋ค.
const obj = reactive({
count: 0
}) // no reference to original
vue๋ primitive value์ ๋ํด์๋ Proxy๋ก ๊ฐ์ธ์ง ์๋๋ค. ๋ฐ๋ผ์ primitive value๋ ===
๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
const obj = reactive({
count: 0
})
console.log(obj.count === 0) // true
์ปดํฌ๋ํธ์ ํ
ํ๋ฆฟ์ render
ํจ์๋ก ์ปดํ์ผ๋๋ค. render ํจ์๋ VNodes
๋ฅผ ์์ฑํ๋ค. VNode๋ ์ปดํฌ๋ํธ๊ฐ ์ด๋ป๊ฒ ๋ ๋๋งํด์ผ ํ๋์ง๋ฅผ ์ค๋ช
ํ๋ค. ๋ทฐ๋ VNodes๋ฅผ effect๋ก ๊ฐ์ธ์, ์คํ๋๋ ๋์ ์ด๋ ํ ํ๋กํผํฐ์ ์ ๊ทผํ์๋์ง๋ฅผ ์์๋ธ๋ค.
render
ํจ์๋ ๊ฐ๋
์ ์ผ๋ก computed
ํ๋กํผํฐ์ ๋งค์ฐ ์ ์ฌํ๋ค. ๋ทฐ๋ ์ ํํ ์ด๋ป๊ฒ dependency๊ฐ ์ฌ์ฉ๋์๋์ง๋ฅผ ์ถ์ ํ์ง ์๋๋ค. ์ค์ง ๊ทธ ํจ์๊ฐ ์คํ๋๋ ๋์ ์ด๋ ํ ์์ ์ dependency๊ฐ ์ฌ์ฉ๋์๋์ง๋ง ์ ์ ์๋ค.
๊ทธ์ค ์ด๋ ํ ํ๋กํผํฐ๊ฐ ๋์ค์ ๋ณ๊ฒฝ์ด๋๋ฉด, effect๋ฅผ ๋ค์ ์คํ์ํค๊ฒ๋ํ๋ค. render
ํจ์๋ฅผ ๋ค์ ์คํ์์ผ์ ์๋ก์ด VNode๋ฅผ ๋ง๋๋ ๊ฒ์ด๋ค. ์ด VNode๋ DOM์ ํ์ํ ๋ณํ๋ฅผ ๋ง๋๋๋ฐ ์ฌ์ฉ๋๋ค.
์๋ฐ์คํฌ๋ฆฝํธ ๊ฐ์ฒด์์ reactive state๋ฅผ ๋ง๋๋ ค๋ฉด, reactive
method๋ฅผ ์ฌ์ฉํด์ผ ํ๋ค.
import { reactive } from 'vue';
const state = reactive({
count: 0,
})
reactive
๋ vue2์ Vue.observable()
API์ ๊ฐ๊ณ , ์ด๋ฆ๋ง ๋ณ๊ฒฝ๋์๋ค. ์ฌ๊ธฐ์ ๋ฐํ๋๋ ๊ฐ์ ๋ฐ์ํ ๊ฐ์ฒด์ด๋ค. ๋ฐ์ํ์ผ๋ก ์ ํ๋๋ ๊ฒ์ "deep"ํ๊ฒ ์ด๋ค์ง๋ค. ์ฆ, ๋ชจ๋ ์ค์ฒฉ๋ ํ๋กํผํฐ๋ค๋ ๋ค ๋ฐ์ํ์ด๋ค.
๋ทฐ์์ ๋ฐ์ํ ์ํ๋ฅผ ์ฌ์ฉํ๋ ์ค์ํ ๊ฒฝ์ฐ๋, ๋ ๋๋งํ ๋ ์ฌ์ฉํ๋ค๋ ๊ฒ์ด๋ค. ์์กด์ฑ์ ์ถ์ ํ๊ธฐ ๋๋ฌธ์, ๋ฐ์ํ ์ํ๊ฐ ๋ณ๊ฒฝ๋๋ฉด, ๋ทฐ๋ ์๋์ผ๋ก ์ ๋ฐ์ดํธ๋๋ค.
data() ํจ์์์ ๊ฐ์ฒด๋ฅผ ๋ฐํํ๋ฉด, ๊ทธ ๊ฐ์ฒด๋ ๋ด๋ถ์ ์ผ๋ก reactive()๋ก ์ธํ์ฌ ๋ฐ์ํ์ด ๋๋ค. render ํจ์๋ก ์ปดํ์ผ ๋๋ ํ ํ๋ฆฟ์ด ๋ฐ์ํ ํ๋กํผํฐ๋ค์ ์ฌ์ฉํ๊ฒ ๋๋ค.
๋
๋ฆฝ์ ์ธ primitive value์ธ String์ ๋ฐ์ํ์ผ๋ก ๋ง๋ค๊ณ ์ถ๋ค๊ณ ๊ฐ์ ํ์. ๋ฌผ๋ก ํ๋์ String ํ๋กํผํฐ๋ฅผ ๊ฐ๋ ๊ฐ์ฒด๋ฅผ ๋ง๋ค์ด์ reactive
์ ๋๊ธฐ๋ฉด ๋๋ค. ๋ทฐ์๋ ์ด์ ๊ฐ์ ์ญํ ์ ํด์ฃผ๋ ๋ฉ์๋๊ฐ ์๋๋ฐ, ์ด๋ฅผ ref
๋ผ๊ณ ํ๋ค.
ref
๋ value
๋ผ๋ ํ๋กํผํฐ ํ๋๋ง ๊ฐ๊ณ ์๋ ๋ฐ์ํ์ด์ ๋ณ๊ฒฝ์ด ๊ฐ๋ฅํ ๊ฐ์ฒด๋ฅผ ๋ฐํํ๋ค.
import { ref } from 'vue'
const count = ref(0)
console.log(count.value) // 0
count.value++
console.log(count.value) // 1
ref๊ฐ render context(setup()์ด ๋ฐํํ๋ ๊ฐ์ฒด)์์ ๋ฆฌํด๋๊ณ , ํ
ํ๋ฆฟ์์ ์ ๊ทผํ๊ฒ ๋ ๋, ์๋์ผ๋ก ๋ด๋ถ์ ๊ฐ์ shallow unwrapํ๋ค. ๋ฐ๋ผ์, ์ค์ฒฉ๋ ref์๋ง .value
๋ฅผ ์ฌ์ฉํ๋ฉด ๋๋ค.
<template>
<div>
<span>{{ count }}</span>
<button @click="count++">Increment count</button>
<button @click="nested.count.value++">Nested Increment count</button>
</div>
</template>
<script>
import { ref } from 'vue'
export default {
setup() {
const count = ref(0)
return {
count,
nested: {
count
}
}
}
}
</script>
TIP ์ค์ ๊ฐ์ฒด์ ์ ๊ทผํ์ง ์์๋ ๋๋ค๋ฉด, reactive๋ก ๊ฐ์ ์ ์๋ค.
nested: reactive({ count })<div>nested: {{nested.count}}</div>
ref
๊ฐ ๋ฐ์ํ ๊ฐ์ฒด์ ํ๋กํผํฐ๋ก์ ์ ๊ทผ๋๊ฑฐ๋ ๋ณ๊ฒฝ๋ ๋, ์๋์ผ๋ก ๋ด๋ถ์ ๊ฐ์ unwrapํ์ฌ ์ผ๋ฐ์ ์ธ ํ๋กํผํฐ์ฒ๋ผ ๋์ํ๋๋ก ํ๋ค.
const count = ref(0)
const state = reactive({
count
})
console.log(state.count) // 0 -> ๊ฐ์ฒด์ ํ๋กํผํฐ๋ก ์ ๊ทผ
state.count = 1
console.log(count.value) // 1 -> ref๋ .value๋ก ๋ด๋ถ ๊ฐ์ ์ ๊ทผํด์ผ ํ๋ค.
Collection Type์์์ ๋ฐ์ํ ์ ๊ทผ -> unwrapping์ด ๋์ง ์์.
- Ref unwrapping์ ๋ฐ์ํ
Object
๋ด๋ถ์์ ์ค์ฒฉ๋ ๊ฒฝ์ฐ์๋ง ์ด๋ฃจ์ด์ง๋ค. - ref๊ฐ
Array
๋Map
์ฒ๋ผ collection type์ธ ๊ฒ๋ค์์ ์ ๊ทผ๋ ๋๋ unwrapping์ด ์ด๋ฃจ์ด์ง์ง ์๋๋ค. ๋ฐ๋ผ์ ์ด๋๋.value
๋ก ๋ด๋ถ์ ๊ฐ์ ์ ๊ทผํด์ผ ํ๋ค.
const books = reactive([ref('value~')]);
console.log(books[0].value); // value~
const map = reactive(new Map([['count', ref(0)]]));
console.log(map.get('count').value); // 0
๋ค์๊ณผ ๊ฐ์ด reactive object๋ฅผ ES6 destructing์ผ๋ก ๊ฐ์ ธ์ค๊ฒ ๋๋ฉด, reactivity๊ฐ ์ฌ๋ผ์ง๊ฒ ๋๋ค.
import { reactive } from 'vue'
const book = reactive({
author: 'Vue Team',
year: '2020',
title: 'Vue 3 Guide',
description: 'You are reading this book right now ;)',
price: 'free'
})
let { author, title } = book // reactivity๊ฐ ์ฌ๋ผ์ง๊ฒ ๋จ
Reactivity๋ฅผ ์ ์งํ๊ธฐ ์ํด์๋ ๋ฐ์ํ ๊ฐ์ฒด๋ฅผ set of refs๋ก ๋ณ๊ฒฝ์์ผ์ผ ํ๋ค.
set of refs๋ก ๋ง๋ค๊ธฐ ์ํด์๋ toRefs
API๋ฅผ ์ฌ์ฉํด์ผ ํ๋ค.
toRefs๋ ๋ฐ์ํ ๊ฐ์ฒด๋ฅผ ๊ฐ ํ๋กํผํฐ๊ฐ ref์ธ plain object๋ก ๋ณ๊ฒฝ์ํจ๋ค.
์ด ref๋ค์ source object์ reative connection์ ์ ์งํ๊ฒ ๋๋ค.
toRefs๋ก ๊ฐ ํ๋กํผํฐ๋ค์ ref๊ฐ ๋์์ผ๋ฏ๋ก, ์ด ๋ด๋ถ ๊ฐ์ ์ ๊ทผํ๊ธฐ ์ํด์๋ .value
๋ฅผ ์ฌ์ฉํด์ผ ํ๋ค.
import { reactive, toRefs } from 'vue'
const book = reactive({
author: 'Vue Team',
year: '2020',
title: 'Vue 3 Guide',
description: 'You are reading this book right now ;)',
price: 'free'
})
let { author, title } = toRefs(book)
title.value = 'Vue 3 Detailed Guide' // we need to use .value as title is a ref now
console.log(book.title) // 'Vue 3 Detailed Guide'
ref, reactive๋ก ๋ฐ์ํ ๊ฐ์ฒด๋ฅผ ๋ง๋ค์ด์ ๋ณ๊ฒฝ์ ์ถ์ ํ๊ฒ ๋๋ค.
๊ทธ๋ฌ๋ ๊ฐ๋์ ์ด๋ ํ ์ง์ ์์ ๋ณํ๋ฅผ ๋ง์์ผํ๋ ๊ฒฝ์ฐ๊ฐ ์๊ธด๋ค.
๋ฐ์ดํ๋ฅผ provideํ ๋, injectํ ์ชฝ์์ ๊ทธ ๊ฐ์ ๋ณ๊ฒฝ ๋ชป์ํค๊ฒ ํด์ผํ ๊ฒฝ์ฐ๊ฐ ์์ ๊ฒ์ด๋ค.
์ด๋ฌํ ๊ฒฝ์ฐ์ readonly๋ก ๊ฐ์ธ์ ๋ณ๊ฒฝ์ํค์ง ๋ชปํ๊ฒ ํด์ผ ํ๋ค.
<script>
import { provide, reactive, readonly, ref } from 'vue'
import MyMarker from './MyMarker.vue'
export default {
components: {
MyMarker
},
setup() {
const location = ref('North Pole')
provide('location', readonly(location))
}
}
</script>
computed property๋ state์ ์์กดํ๊ณ ์๋ state์ด๋ค. computed๋ฅผ ๋ง๋ค๋ ค๋ฉด, computed
method๋ฅผ ์ฌ์ฉํด์ผ ํ๋ค.
computed computed method๋ getter ํจ์๋ฅผ ๋ฐ์์, getter๊ฐ ๋ฐํํ ๊ฐ์์ ๋ถ๋ณ ๋ฐ์ํ ref ๊ฐ์ฒด๋ฅผ ๋ง๋ค์ด ๋ฐํํ๋ค.
computed๊ฐ์ readonly๋ค. ๋ฐ๋ผ์, ์ด ๊ฐ์ ๋ณ๊ฒฝ์ํค๋ ๊ฒ์ ํ ์ ์๋ค.
const count = ref(1)
const plusOne = computed(() => count.value + 1)
console.log(plusOne.value) // 2
plusOne.value++ // Write operation failed: computed value is readonly
- computed์์ Writable ref object ๋ฐํ๋ฐ๊ธฐ
computed๊ฐ get
, set
ํจ์๊ฐ ์๋ ๊ฐ์ฒด๋ฅผ ๋ฐ์ผ๋ฉด, writable ref ๊ฐ์ฒด๊ฐ ๋๋ค.
const count = ref(1);
const plusOne = computed(() => count.value + 1);
const plusTen = computed({
get: () => count.value + 10,
set: (val) => {
count.value = val;
},
});
console.log(plusOne.value); //2
console.log(plusTen.value); //11
plusTen.value = 100;
console.log(count.value); //100
๋ฐ์ํ state์ ๋ฐ๋ผ side effect๋ฅผ ์๋์ผ๋ก ์คํ์ํค๊ธฐ ์ํด์, watchEffect
๋ฉ์๋๋ฅผ ์ฌ์ฉํ ์ ์๋ค. watchEffect๋ dependency๋ฅผ trackingํ ๋ ์ฆ์ ์คํ๋๊ณ , dependency๊ฐ ๋ณ๊ฒฝ๋ ๋๋ง๋ค ํจ์๋ฅผ ๋ค์ ์คํ์ํจ๋ค.
watchEffect()๋ DOM์ด ๋ง์ดํธ๊ฑฐ๋ ์ ๋ฐ์ดํธ ๋๊ธฐ ์ ์ ์คํ๋๋ค.
const count = ref(0);
watchEffect(() => console.log(count.value)); // logs 0 : dependency trackingํ๋ฉฐ ์ฆ์ ์คํ
setTimeout(() => {
count.value++; // logs 1 -> dependency ๋ณ๊ฒฝ๋๋ฉฐ ๋ค์ ์คํ
}, 100);
watchEffect
๊ฐ setup()
์ด๋ lifecycle hooks
์์ ํธ์ถ๋ ๋, watcher๋ ์ปดํฌ๋ํธ์ ๋ผ์ดํ์ฌ์ดํด๊ณผ ์ฐ๊ฒฐ๋๋ฉฐ, ์ปดํฌ๋ํธ๊ฐ ์ธ๋ง์ดํธ๋ ๋ ์๋์ผ๋ก ๋ฉ์ถฐ์ง๋ค.
๋ช
์์ ์ผ๋ก Watcher๋ฅผ ๋ฉ์ถ๊ณ ์ถ์ ๊ฒฝ์ฐ, watchEffect
์ ๋ฐํ๊ฐ์ ์ด์ฉํ๋ฉด ๋๋ค. ์ด ๋ฐํ๊ฐ์ stop handler์ธ๋ฐ, ์ด๋ฅผ ํธ์ถํ์ฌ watcher๋ฅผ ๋ฉ์ถ ์ ์๋ค.
setup() {
const count = ref(0);
const stop = watchEffect(() => console.log(count.value)); // logs 0
stop(); // stop watcher
setTimeout(() => {
count.value++; // Watcher๊ฐ ๋ฉ์ถฐ์ก๊ธฐ์, ๋ก๊ทธ 1์ด ์ฐํ์ง ์๋๋ค.
}, 100);
},
watched effect ํจ์๊ฐ ๋น๋๊ธฐ side effect๋ฅผ ์ํํ ๊ฒฝ์ฐ๊ฐ ์๋ค. ๊ทธ ๋น๋๊ธฐ ์ฌ์ด๋ ์ดํํธ๋ invalidateํ ๊ฒฝ์ฐ๊ฐ ์๊ธฐ๋ฉด, ํด๋ฆฐ์ ๋์ด์ผ ํ๋ค.
Effect ํจ์๋ onInvalidate
ํจ์๋ฅผ ๋ฐ๋๋ฐ, ์ด ํจ์๋ invalidation callback์ผ๋ก ๋ฑ๋ก๋๋ค.
invalidation callback์ ๋ค์๊ณผ ๊ฐ์ ๊ฒฝ์ฐ์ ํธ์ถ๋๋ค.
- effect๊ฐ ๋ค์ ์คํ๋๋ ์์
- watcher๊ฐ ๋ฉ์ถ๋ ์์ (i.e. watchEffect๊ฐ setup์ด๋ lifecycle hook์ ๋ฑ๋ก๋์์ ๋, ์ปดํฌ๋ํธ๊ฐ ์ธ๋ง์ดํธ๋๋ ์์ )
invalidation callback์ ํจ์์ ์ธ์๋ก ๋๊ฒจ์ ๋ฑ๋กํ์๋ค. ์๋ํ๋ฉด ๋ฆฌํด๊ฐ์ด async ์๋ฌ ํธ๋ค๋ง์ ์ค์ํ๊ธฐ ๋๋ฌธ์ด๋ค.
๋ค์ ์๊ฐ, data fetchingํ ๋, async effect function์ ํํ ์์ด๋ค.
const data = ref(null)
watchEffect(async (onInvalidate) => {
onInvalidate(() => { /* ... */ }) // we register cleanup function before Promise resolves
data.value = await fetchData(props.id)
})
async ํจ์๋ ๋ด์ฌ์ ์ผ๋ก ํ๋ผ๋ฏธ์ค๋ฅผ ๋ฐํํ๋๋ฐ, ํด๋ฆฐ์ ํจ์๋ ํ๋ผ๋ฏธ์ค๊ฐ resolve๋๊ธฐ ์ ์ ๋ฑ๋ก๋์ด์ผ ํ๋ค. ๋ํ, ๋ทฐ๋ ํ๋ผ๋ฏธ์ค ์ฒด์ธ์์ ๋ฐ์ํ ์ ์๋ ์๋ฌ๋ฅผ ์๋์ผ๋ก ์ฒ๋ฆฌํ๊ธฐ ์ํด ๋ฐํ๋๋ ํ๋ผ๋ฏธ์ค์ ์์กดํ๋ค.
In addition, Vue relies on the returned Promise to automatically handle potential errors in the Promise chain.