Intro
์๋ ํ์ธ์. ๊ฐ๋ฐ์ ์์ฑ๋น์ ๋๋ค. ์ค๋์ Dynamic Components์ ์ด์ด์ Vue์ Reactivity ์์คํ ์ ๋ํด์ ์์๋ณด๋ ค ํฉ๋๋ค. ๊ทธ์ ๋จธ๋ฆฟ์์๋ง ์๋ ๊ฑฐ๋ณด๋จ ๊ธ๋ก ๋จ๊ธฐ๋ ๊ฒ ๊ธฐ์ต๋ ์ ๋๊ณ , ๊ธ์ ์ฐ๋ฉด์๋ ๋ ์ฐพ์๋ณด๊ฒ ๋๋ ์ฐธ ์ข์ ๊ฑฐ ๊ฐ์์. ํ์ง๋ง ์ญ์ ๊ธ์ ์ฐ๋ ๊ฒ ์ฝ์ง ์๋ค์.
Vue Reactivity
Vue์ ์ฅ์ ์ด๋ผ๊ณ ํ๋ฉด ๊ผญ ๋ฑ์ฅํ๋๊ฒ ์์ต๋๋ค. ๋ฐ๋ก Data Binding์ ๋๋ค. Vue์ ๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ์ ํ๋ฉด์ ํ์๋๋ ๊ฐ๊ณผ ๋ฐ์ดํฐ ๊ฐ์ด ์ฐ๊ฒฐ์ด ๋์ด ์๋ก๋ฅผ ๋ณ๊ฒฝ์ํค๊ธฐ ๋๋ฌธ์ ์๋ฐฉํฅ ๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ์ด๋ผ๊ณ ๋ ํฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ ์ด ์๋ฐฉํฅ ๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ์ด ๊ฐ๋ฅํ ์ด์ ๊ฐ Vue์ Reactivity ์์คํ ๋๋ถ์ ๋๋ค.
Vue Guide Reactivity in Depth ์ค ์ผ๋ถ
Every component instance has a corresponding watcher instance, which records any properties “touched” during the component’s render as dependencies. Later on when a dependency’s setter is triggered, it notifies the watcher, which in turn causes the component to re-render.
Vue ๊ณต์ ๊ฐ์ด๋์์ Reactivity ์์คํ ์ ์ค๋ช ํด์ฃผ๋ ํ์ด์ง์ ์ผ๋ถ๋ฅผ ๊ฐ์ ธ์๋ดค์ต๋๋ค. ์ด ๋ถ๋ถ์ ํ๊ธ ๋ฒ์ญ๋ณธ๋ณด๋จ ์์ด ์ชฝ์ด ์ข ๋ ์ดํดํ๊ธฐ ์ฝ๋๊ตฐ์. ์งง์ ๊ธ์ด์ง๋ง ์ฌ์ค Vue์ Reactivity ์์คํ ์ ๊ดํตํ๋ ๋ด์ฉ์ ๋ชจ๋ ๋ค์ด๊ฐ ์์ต๋๋ค.
1. ๋ชจ๋ ์ปดํฌ๋ํธ ์ธ์คํด์ค๋ watcher ์ธ์คํด์ค๋ฅผ ๊ฐ์ง๋ค.
2. watcher ์ธ์คํด์ค๋ touched๋ properties๋ฅผ ๊ธฐ๋กํ๋ค.
3. setter๊ฐ ํธ๋ฆฌ๊ฑฐ ๋๋ฉด watcher์ Notify. ์ปดํฌ๋ํธ๊ฐ ๋ค์ ๋๋๋ง ๋๋ค.
๊ทธ ๋ฐ์๋ ๊ทธ๋ฆผ์ผ๋ก๋ Vue์ Reactivity ์์คํ ์ ๋ณด์ค ์๋ ์์ต๋๋ค.
Vue์ Reactivity ์์คํ ์ ๋ํ ์ดํด๋ ์งง์ ๊ธ๊ณผ ํ๋์ ๊ทธ๋ฆผ์ผ๋ก๋ ์ฌ์ค ์ถฉ๋ถํฉ๋๋ค. ํ์ง๋ง ์ ๋ ์ค์ ๋ก ์ด ์์คํ ์ด ์ด๋ป๊ฒ ๊ตฌํ๋์ด ์์๊น๊ฐ ํ๋ฒ ๋ ๊ถ๊ธํ์ต๋๋ค. ๊ทธ๋์ ์ฐจ๋ก๋๋ก ์ฝ์์ ์ฐ์ด๋ณด๊ฑฐ๋ ์์ค ์ฝ๋๋ฅผ ๋ถ์ํ๋ฉฐ ์์๋ดค์ต๋๋ค.
์ ๊ฐ ๋ถ์ํ๋ ๊ฒฐ๊ณผ๋ฅผ ์๋ ๊ทธ๋ฆผ์ ๋ น์ฌ๋ดค์ต๋๋ค. Data์ getter์ setter๊ฐ reactiveGetter์ reactiveSetter๋ก ๋ฐ๋๊ณ (vue/src/core/observer/index.js), Watcher ์์๋ deps๋ผ๋ ์์ฑ์ ์๊ฒผ์ต๋๋ค(vue/src/core/observer/watcher.js). ๊ทธ๋ฆฌ๊ณ ์๋ ๊ทธ๋ฆผ์ผ๋ก๋ ์ ์ ์์๋ Dep์ด๋ ๊ฐ์ฒด์ ๋ํด ์๊ฒ๋์์ฃ .(vue/src/core/observer/dep.js)
/**
* A dep is an observable that can have multiple
* directives subscribing to it.
*/
export default class Dep {
// ...
depend () {
if (Dep.target) {
Dep.target.addDep(this)
}
}
notify () {
// stabilize the subscriber list first
const subs = this.subs.slice()
if (process.env.NODE_ENV !== 'production' && !config.async) {
// subs aren't sorted in scheduler if not running async
// we need to sort them now to make sure they fire in correct
// order
subs.sort((a, b) => a.id - b.id)
}
for (let i = 0, l = subs.length; i < l; i++) {
subs[i].update()
}
}
}
๊ทธ์ค์์๋ reactiveGetter(ํน์ reacitveSetter)์์ Object.defineProperty๋ฅผ ํ์ฉํ๋ ๋ฐฉ๋ฒ์ ์ ๊ธฐํ์ต๋๋ค. ์๊ณ ๋ ์์๋ ํจ์์์ง๋ง ‘์~ ์ด๋ ๊ฒ ์ฌ์ฉํ๋ฉด ๋๋๊ตฌ๋’ํ๊ฒ ๋๋ ๋ถ๋ถ์ด์์ต๋๋ค.(์ด ๋ถ๋ถ์ ๋ ๋ค๋ฅธ ๊ธ์์ ์ ์ด๋ณผ๊ฒ์!)
Computed
computed: {
componentLoader() {
return () => import(`@/components/${this.currentTab}`)
},
}
๊ทธ๋ ๋ค๋ฉด ์ด์ ์ ๋ณด์๋ Computed๋ ์ด์งธ์ ์ด Watcher ๊ฐ์ฒด์ ๊ธฐ๋ก์ด ๋์ง ์์์๊น์? ์ฌ์ค ์ด ๋ถ๋ถ์ ํด๋ต์ ์ป๊ธฐ ์ํด์๋ ์์์ ์์๋๋ ๋ถ๋ถ๋ค๋ง์ผ๋ก๋ ๋ถ์กฑํ์ต๋๋ค. Computed๊ฐ ์ด๋ป๊ฒ ์์ฑ๋๋์ง๋ฅผ ๋ด์ผ ๊ฐ๋ฅํ์ฃ . ๊ฒฐ๋ก ๋ถํฐ ์ด์ผ๊ธฐํด๋๋ฆฌ๋ฉด ‘Touch๊ฐ ์ผ์ด๋๋ ์์ ์ ์ฐจ์ด’๊ฐ ๊ทธ ์์ธ์ ๋๋ค.
computed: {
CASE1() {
return () => this.currentTab
},
CASE2() {
return this.currentTab
}
}
์ดํด๋ฅผ ๋๊ธฐ ์ํด ๊ฐ๋จํ ์ฝ๋๋ฅผ ๊ฐ์ ธ์๋ดค์ต๋๋ค. CASE 1์ componentLoader์ ๋ง์ฐฌ๊ฐ์ง๋ก ์ ๋๋ก ๋์ํ์ง ๋ชปํฉ๋๋ค. ํ์ง๋ง CASE2๋ ์ ์ ๋์ํ์ฃ . ์ฐจ์ด์ ์ CASE1์ ํจ์๋ฅผ return ํ๋ค๋ ์ ์ ๋๋ค. ๋ฐ๋ก ์ด ๋ถ๋ถ ๋๋ฌธ์ ์ฝ๋๊ฐ ์คํ๋๋ ์์ ์ด ์ฐจ์ด๊ฐ ๋๊ฒ ๋ฉ๋๋ค.
์ค์ ๋ก CASE 1๊ณผ ๊ฐ์ coponentLoader๋ฅผ ์ฐ์ด๋ณด๋ฉด ์ด๋ฐ ๋ชจ์ต์ผ๋ก ๋ณด์ ๋๋ค. computedLoader๊ฐ ํธ์ถ๋ ๋์๋ ์ฆ, Vue์ Computed๊ฐ ๋ฑ๋ก๋ ๋์๋ ์์ง ์ฝ๋๊ฐ ์คํ์ด ๋์ง ์์ ๊ฑฐ์ฃ .
Ending
Vue์ Reactivity ์์คํ ์ ์์๋ดค์ต๋๋ค๋ง ์ด๊ฒ๋ณด๋ค ํจ์ฌ ๋ณต์กํ ๊ธฐ๋ฅ๋ค์ด ์จ์ด์๊ฒ ์ง์. ๊ทธ๋๋ ์ด๋ ๊ฒ ๋ถ์ํ๋ฉด์ ์ด๋ป๊ฒ? ์?๋ผ๋ ์๋ฌธ์ ๋ ํ๋ ธ๊ณ , ‘์ด๋ฐ ์์ผ๋ก ์ฌ์ฉํ ์ ์๊ตฌ๋’๋ผ๋ ์ข์ ๊ฒฝํ์ ์ป๊ฒ ๋ ์๊ฐ์ด์์ต๋๋ค.
(์ด ๊ธ์์ ํ์ฉํ๋ ์์ค ์ฝ๋๋ ์ด๊ณณ์์ ํ์ธ ๊ฐ๋ฅํ์ญ๋๋ค.)
๋๊ธ