Vue - Component State Flow

jun_grammer·2024년 5월 7일

Vue.js

목록 보기
3/5
post-thumbnail

Passing Props

Props

같은 데이터 하지만 다른 컴포넌트

  • 동일한 사진 데이터가 한 화면에 다양한 위치에서 여러번 출력될 때
  • 사진을 변경 해야 할 때 모든 컴포넌트에 대해 변경 요청을 해야 함

-> "공통된 부모 컴포넌트에서 관리하자"


부모는 자식에게 데이터를 전달(Pass Props)하며,
자식은 자신에게 일어난 일을 부모에게 알림(Emit event)

Props

부모 컴포넌트로부터 자식 컴포넌트로 데이터를 전달하는 데 사용되는 속성

Props 특징

  • 부모 특성이 업데이트되면 자식으로 전달 되지만 그 반대는 안됨
  • 즉, 자식 컴포넌트 내부에서 props를 변경하려고 시도해서는 안되며 불가능
  • 또한 부모 컴포넌트가 업데이트될 때마다 이를 사용하는 자식 컴포넌트의 모든 props가 최신 값으로 업데이트 됨

-> 부모 컴포넌트에서만 변경하고 이를 내려 받는 자식 컴포넌트는 자연스럽게 갱신

One-Way Datae Flow

모든 props는 자식 속성과 부모 속성 사이에
하향식 단방향 바인딩을 형성
(one-way-down binding)

단방향인 이유

  • 하위 컴포넌트가 실수로 상위 컴포넌트의 상태를 변경하여 앱에서의
    데이터 흐름을 이해하기 어렵게 만드는 것을 방지하기 위함

-> 데이터 흐름의 "일관성" 및 "단순화"

Props 선언

사전 준비

  1. vue 프로젝트 생성
  2. 초기 생성된 컴포넌트 모두 삭제 (App.vue 제외)
  3. src/assets 내부 파일 모두 삭제
  4. main.js 해당 코드 삭제
// main.js

import './assets/main.css'
  1. App > Parent > ParentChild 컴포넌트 관계 작성
  • App 컴포넌트 작성
<!-- App.vue -->

<template>
  <div>
    <Parent />
  </div>
</template>

<script setup>
import Parent from '@/components/Parent.vue'
</script>
  • Parent 컴포넌트 작성
<!-- Parent.vue -->

<template>
  <div>
    <ParentChild />
  </div>
</template>

<script setup>
import ParentChild from '@/components/ParentChild.vue'
</script>
  • ParentChild 컴포넌트 작성
<!-- ParentChild.vue -->

<template>
  <div></div>
</template>

<script setup>
</script>

Props 선언

부모 컴포넌트에서 내려 보낸 props를 사용하기 위해서는
자식 컴포넌트에서 명시적인 props 선언이 필요

Props 작성

  • 부모 컴포넌트 Parent에서 자식 컴포넌트 ParentChild에 보낼 props 작성
[my-msg]="[message]"
[props 이름] [props 값]
<template>
  <div>
    <ParentChild my-msg="message" />
  </div>
</template>

Props 선언

  • defineProps()를 사용하여 props를 선언
  • defineProps()에 작성하는 인자의 데이터 타입에 따라 선언 방식이 나뉨
<script setup>
defineProps()
</script>

Props 선언 2가지 방식

  1. "문자열 배열"을 사용한 선언
  2. "객체"를 사용한 선언

1. 문자열 배열을 사용한 선언

  • 배열의 문자열 요소로 props 선언
  • 문자열 요소의 이름은 전달된 props의 이름
<!-- ParentChild.vue -->

<script setup>
defineProps(['myMsg'])
</script>

2. 객체를 사용한 선언

  • 각 객체 속성의 키가 전달받은 props 이름이 되며, 객체 속성의 값은
    값이 될 데이터의 타입에 해당하는 생성자 함수(Number, String...)여야 함

※ 객체 선언 문법 사용 권장

<!-- ParentChild.vue -->

<script setup>
defineProps({
  'myMsg': String
})
</script>

props 데이터 사용

  • props 선언 후 템플릿에서 반응형 변수와 같은 방식으로 활용
<!-- ParentChild.vue -->

<div>
  <p>{{ myMsg }}</P>
</div>
  • props를 객체로 반환하므로 필요한 경우 JavaScript에서 접근 가능
<script setup>
const props = defineProps({ myMsg: String })

console.log(props) // {myMsg: 'message'}
console.log(props.myMsg) // 'message'
</script>

한 단계 더 props 내려 보내기

  • ParentChild 컴포넌트를 부모로 갖는 ParentGrandChild 컴포넌트 생성 및 등록
<!-- ParentGrandChild.vue -->

<template>
  <div>
  </div>
</template>

<script setup>
</script>
<!-- ParentChild.vue -->

<template>
  <div>
    <p>{{ myMsg }}</p>
    <ParentGrandChild />
  </div>
</template>

<script setup>
import ParentGrandChild from '@/components/ParentGrandChild.vue'

defineProps({
  myMsg: String,
})
</script>

  • ParentChild 컴포넌트에서 Parent로부터 받은 props인 myMsg를 ParentGrandChild에게 전달
<!-- ParentChild.vue -->

<template>
  <div>
    <p>{{ myMsg }}</p>
    <ParentGrandChild :my-msg="myMsg" />
  </div>
</template>
<!-- ParentGrandChild.vue -->

<template>
  <div>
    <p>{{ myMsg }}</p>
  </div>
</template>

<script setup>
defineProps({
  myMsg: String
})
</script>

Props 세부사항

Props 세부사항

  1. Props Name Casing (Props 이름 컨벤션)
  2. Static Props 와 Dynamic Props

1. Props Name Casing

  • 자식 컴포넌트로 전달 시 (->kebab-case)
<ParentChild my-msg="message" />
  • 선언 및 템플릿 참조 시 (->camelCase)
defineProps({
  myMsg: String
})
<p>{{ myMsg }}</p>

2. Static props & Dynamic props

  • 지금까지 작성한 것은 Static(정적) props
  • v-bind를 사용하여 동적으로 할당된 props를 사용할 수 있음
  1. Dynamic props 정의
// Parent.vue 

import { ref } from 'vue'

const name = ref('Alice')
<!-- Parent.vue -->

<ParentChild my-msg="message" :dynamic-props="name" />

  1. Dynamic props 선언 및 출력
// ParentChild.vue

defineProps({
  myMsg: String,
  dynamicProps: String,
})
<!-- ParentChild.vue -->

<p>{{ dynamicProps }}</p>

Props 활용

다른 디렉티브와 함께 사용

  • v-for와 함께 사용하여 반복되는 요소를 props로 전달하기

  • ParentItem 컴포넌트 생성 및 Parent의 하위 컴포넌트로 등록

<!-- ParentItem.vue -->

<template>
  <div>
  </div>
</template>

<script setup>
</script>
<!-- Parent.vue -->

<template>
  <div>
    <ParentItem />
  </div>
</template>

<script setup>
import ParentItem from '@/components/ParentItem.vue'
</script>
  • 데이터 정의 및 v-for 디렉티브의 반복 요소로 활용
  • 각 반복 요소를 props로 내려 보내기
// Parent.vue

const items = ref([
  { id: 1, name: '사과' },
  { id: 2, name: '바나나' },
  { id: 3, name: '딸기' }
])
<!-- Parent.vue -->

<ParentItem 
  v-for="item in items"
  :key="item.id"
  :my-prop="item"
/>
  • props 선언 및 출력 결과 확인
<template>
  <div>
    <p>{{ myProp.id }}</p>
    <p>{{ myProp.name }}</p>
  </div>
</template>

<script setup>
defineProps({
  myProp: Object
})
</script>

Component Events

Emit

부모는 자식에게 데이터를 전달(Pass Props)하며,
자식은 자신에게 일어난 일을 부모에게 알림(Emit event)
-> 부모가 props 데이터를 변경하도록 소리쳐야 한다.

$emit()

자식 컴포넌트가 이벤트를 발생시켜 부모 컴포넌트로 데이터를 전달하는 역할의 메서드

※ '$'표기는 Vue 인스턴스의 내부 변수들을 가리킴
※ Life cycle hooks, 인스턴스 메서드 등 내부 특정 속성에 접글할 때 사용

emit 메서드 구조

$emit(event, ...args)

  • event
    • 커스텀 이벤트 이름
  • args
    • 추가 인자

이벤트 발신 및 수신

이벤트 발신 및 수신 (Emitting and Listening to Events)

  • $emit을 사용하여 템플릿 표현식에서 직접 사용자 적의 이벤트를 발신
<button @click="$emit('soneEvent')">클릭</button>
  • 그런 다음 부모는 v-on을 사용하여 수신할 수 있음
<ParentComp @some-event="someCallback" />

이벤트 발신 및 수신하기

  • ParentChild에서 someEvent라는 이름의 사용자 정의 이벤트를 발신
<!-- ParentChild.vue -->

<button @click="$emit('someEvent')">클릭</button>
  • ParentChild의 부모 Parent는 v-on을 사용하여 발신된 이벤트를 수신
  • 수신 후 처리할 로직 및 콜백함수 호출
<!-- Parent.vue -->

<ParentChild 
   @some-event="someCallback"
   my-msg="message" 
   :dynamic-props="name"
/>
// Parent.vue 

const someCallback = function () {
  console.log('ParentChild가 발신한 이벤트를 수신했다!')
}
  • 이벤트 수신 결과

emit 이벤트 선언

emit 이벤트 선언

  • defineEmits()를 사용하여 발신할 이벤트를 선언
  • props와 마찬가지로 defineEmits()에 작성하는 인자의 데이터 타입에 따라 선언 방식이 나뉨 (배열, 객체)
  • defineEmits()는 $emit 대신 사용할 수 있는 동등한 함수를 반환
    (script에서는 $emit 메서드를 접근할 수 없기 때문)
<script setup>
defineEmits()
</script>
<script setup>
const emit = defineEmits(['someEvent', 'myFocus'])
  
const buttonClick = function () {
  emit('someEvent')
}
</script>
  • 이벤트 선언 방식으로 추가 버튼 작성 및 결과 확인
<!-- ParentChild.vue -->

<template>
  <button @click="buttonClick">클릭</button>
</template>


<script setup>
const emit = defineEmits(['someEvent'])
  
const buttonClick = function () {
  emit('someEvent')
}
</script>

이벤트 전달

이벤트 인자 (Event Arguments)

  • 이벤트 발신 시 추가 인자를 전달하여 값을 제공할 수 있음

이벤트 인자 전달 활용

  • ParentChild에서 이벤트를 발신하여 Parent로 추가 인자 전달하기
<!-- ParentChild.vue -->

<template>
  <button @click="emitArgs">추가 인자 전달</button>
</template>


<script setup>
const emit = defineEmits(['someEvent', 'emitArgs'])
  
const buttonClick = function () {
  emit('emitArgs', 1, 2, 3)
}
</script>
  • ParentChild에서 발신한 이벤트를 Parent에서 수신
<!-- Parent.vue -->

<template>
  <ParentChild 
    @some-event="someCallback"
    @emit-args="getNumbers"
    my-msg="message" 
    :dynamic-props="name"
  />
</template>

<script setup>
const getNumbers = function (...args) {
  console.log(args)
  console.log(`ParentChild가 전달한 추가인자 ${args}를 수신했어요.`)
}
</script>
  • 추가 인자 전달 확인

이벤트 세부사항

Event Name Casing

  • 선언 및 발신 시 (-> camelCase)
<button @click="$emit('someEvent')">버튼1</button>
const emit = defineEmits(['someEvent'])

emit('someEvent')
  • 부모 컴포넌트에서 수신 시 (-> kebab-case)
<ParentChild @someEvent='...' />
profile
게임개발자가 되는 그날까지

0개의 댓글