
<ChildComponent :ref="ChildComponentRef" />자식 컴포넌트를 ref로 참조해 해당 컴포넌트의 DOM에 접근하려 했지만,
부모 컴포넌트에서 접근할 수 없었다.
이 문제를 계기로 Vue의 라이프사이클을 다시 한번 정리해보려 한다. (사실 라이프 사이클과는 별개임)
<script setup>을 사용하는 컴포넌트는 기본적으로 닫혀 있습니다. 즉, 템플릿 참조 또는 $parent 체인을 통해 검색>되는 컴포넌트의 공개 인스턴스는<script setup>내부에서 선언된 바인딩을 노출하지 않습니다.
vue 공식 문서를 보면 defineExpose()에 대한 설명으로 위와 같이 나와있음
즉. Vue 3의 보안 정책으로

공식 문서에서도 이 부분은 충분히 직관적으로 설명되어 있음.
public/index.html이 브라우저에 의해 먼저 로드됨
이 안에 <div id="app"></div>와 <script type="module" src="/src/app.js"> 같은 초기 진입점이 있음
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
const app = createApp(App)
app.use(router)
app.mount('#app')
#app에 컴포넌트를 마운트하여 Vue의 제어가 시작됨// App.vue
<template>
<router-view />
</template>
router-view는 현재 URL에 매칭되는 라우트 객체에 등록된 컴포넌트를 실제로 화면에 렌더링하는 영역
vue-router는 현재 URL을 기준으로 라우트를 매칭해서 router-view에 해당 컴포넌트를 동적으로 렌더링함
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
const routes = [
{
path: '/',
component: HomeView
},
{
path: '/about',
component: () => import('../views/AboutView.vue')
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
<!-- 예시: HomeView.vue -->
<template>
<div>{{ message }}</div>
</template>
<script setup>
import { ref, onBeforeMount, onMounted } from 'vue'
const message = ref('Hello!')
const CONSTANT_STATE = 'test'
console.log('script mounted!')
onBeforeMount(() => {
console.log('before mounted!')
})
onMounted(() => {
console.log('mounted!')
})
</script>
<style scoped>
div {
color: blue;
}
</style>
<script setup> 실행import { ref, onBeforeMount, onMounted } from 'vue'
const message = ref('Hello!')
const CONSTANT_STATE = 'test'
console.log('script mounted!')
출력
script mounted!
템플릿(<template>) 컴파일됨
setup() 함수에서 선언한 변수들이 템플릿에서 사용될 수 있도록 바인딩
아직 실제 DOM에 그려진 것은 아님 (가상DOM 형태)
실제 DOM에 컴포넌트가 붙기 직전 (가상 DOM은 준비된 상태)
// DOM에 접근 불가
onBeforeMount(() => {
console.log('before mounted!')
})
onMounted(() => {
console.log('onMounted - 이제 DOM 있음!');
});
반응형 상태 변경시
watch는 반응형 변수의 값이 바뀔 때 바로 실행 (onBeforeUpdate 보다 먼저 호출)
호출 시점의 컨텍스트 이후에 실행되기 때문에 onMounted(), onUpdated() 내부에서 호출해주면 됨
-> 두 훅 모두 돔이 랜더링 된 이후에 호출
nextTick()이 항상 DOM 렌더링 이후 호출된다고 생각하는 경우가 많음
정확히는 Vue의 현재 반응형 업데이트 및 렌더링 큐 작업이 끝난 다음 틱에서 실행됨
따라서 호출 시점의 컨텍스트 이후에 실행됨, 반드시 DOM이 렌더링된 이후라는 보장은 컨텍스트에 따라 달라질 수 있음
ex) PageA에서 router.push('/page-b')로 이동할 때
브라우저 HTML 요청
→ index.html 로드
→ app.js 실행
→ createApp(App).mount('#app')
→ 루트 컴포넌트 렌더링
→ <script setup> 실행
→ ref / 상수 선언
→ setup() 실행
→ 템플릿 컴파일 (가상 DOM 생성)
→ onBeforeMount()
→ 실제 DOM 렌더링
→ onMounted() ← (이 시점부터 DOM 접근 가능)
→ 반응형 데이터 변경
→ onBeforeUpdate()
→ DOM 업데이트
→ onUpdated()
→ router.push('/다른-페이지')
→ onBeforeUnmount()
→ onUnmounted()