- 컴포넌트 기초2024년 05월 29일 18시 50분 37초에 업로드 된 글입니다.작성자: 원2728x90반응형
컴포넌트를 사용하면 UI를 독립적이고 재사용 가능한 일부분으로 분할하고 각 부분을 개별적으로
다룰 수 있음 -> 앱이 중첩된 컴포넌트의 트리로 구성되는 것은 일반적
https://ko.vuejs.org/guide/essentials/component-basics.html 이것은 기본 HTML 엘리먼트를 중첩하는 방법과 매우 유사하지만,
Vue 는 각 컴포넌트에 사용자 정의 컨텐츠와 논리를 캡슐화할 수 있는 자체 컴포넌트 모델을 구현함
컴포넌트 정의하기
빌드 방식을 사용할 때 일반적으로 싱글 파일 컴포넌트(SFC) 라고 하는 .vue 확장자를 사용하는 전용 파일에 각 Vue 컴포넌트를 정의
<script setup> import { ref } from 'vue' const count = ref(0) </script> <template> <button @click="count++">당신은 {{ count }} 번 클릭했습니다.</button> </template>
빌드 방식을 사용하지 않을 때 Vue 컴포넌트는 Vue 관련 옵션을 포함하는 일반 Js 객체로 정의할 수 있음
import { ref } from 'vue' export default { setup() { const count = ref(0) return { count } }, template: ` <button @click="count++"> 당신은 {{ count }} 번 클릭했습니다. </button>` // DOM 내의 템플릿을 대상으로 할 수도 있습니다: // template: '#my-template-element' }
Js 문자열로 정의한 템플릿은 Vue 가 즉석에서 컴파일 함
엘리먼트(기본 <template> 엘리먼트)를 가리키는 ID 셀렉터를 사용할 수도 있음, Vue는 해당 컨텐츠를 템플릿 소스로 사용
위의 예는 단일 컴포넌트를 정의하고 이를 .js 파일의 내보내기 기본 값으로 내보내지만 명명된 내보내기를 사용하여 한 파일에서 여러 개의 컴포넌트로 내보낼 수 있음
컴포넌트 사용하기 (SFC 문법사용)
자식 컴포넌트를 사용하려면 부모 컴포넌트에서 가져와야함 (import)
파일안에 ButtonCounter.vue라는 카운터 컴포넌트를 배치했다고 가정하면, 해당 컴포넌트 파일의 기본 내보내기가 노출
<script setup> 을 사용하면 가져온 컴포넌트를 템플릿에서 자동으로 사용
<script setup> import ButtonCounter from './ButtonCounter.vue' </script> <template> <h1>아래에 자식 컴포넌트가 있습니다.</h1> <ButtonCounter /> </template>
ButtonCounter.vue
<script setup> import {ref} from "vue"; const count = ref(0) </script> <template> <button @click="count++"> 당신은 {{ count }} 번 클릭했다 </button> </template> <style scoped> </style>
컴포넌트는 원하는 만큼 재사용 가능
<h1>여기에 많은 자식 컴포넌트가 있습니다!</h1> <ButtonCounter /> <ButtonCounter /> <ButtonCounter />
버튼을 클릭할 때 각 버튼은 독립적인 count 를 유지 -> 컴포넌트를 사용할 때마다 해당 컴포넌트의 새 인스턴스가 생성됨
SFC에서는 네이티브 HTML 엘리먼트와 구별하기 위해 자식 컴포넌트에 PascalCase 태그 이름을 사용하는 것이 좋음
기본 HTML 태그 이름은 대소문자를 구분하지 않지만, Vue 의 SFC는 컴파일된 포맷으로 대소문자를 구분하여 태그 이름을 사용할 수 있음, 또 /> 를 사용하여 태그를 닫을 수 있음
템플릿을 DOM에서 직접 작성하는 경우 (ex. 기본 <template> 엘리먼트의 컨텐츠) 템플릿은 브라우저의 기본 HTML 구문분석 동작을 따름. 이러한 경우 컴포넌트는 kebab-case 및 명시적 닫는 태그를 사용해야함
<!-- 이 템플릿이 DOM에 작성된 경우 --> <button-counter></button-counter> <button-counter></button-counter> <button-counter></button-counter>
Props 전달하기
블로그를 구축하는 경우 블로그 게시물을 나타내는 컴포넌트가 필요할 수 있음
모든 블로그 게시물이 동일한 시각적 레이아웃을 공유하기를 원하지만 컨텐츠는 다름
props -> 사용할 컴포넌트에 표시하려는 특정 게시물의 제목 및 컨텐츠와 같은 데이터를 전달함
props -> 컴포넌트에 등록할 수 있는 사용자 정의 속성
블로그 게시물 제목을 컴포넌트에 전달하려면 defindProps를 사용
<script setup>//BolgPost.vue defineProps(['title']) </script> <template> <h4>{{ title }}</h4> </template> <style scoped> </style>
* 만약 defineprops is not defined 가 뜬다면 package.json 에가서 아래처럼 설정해주면 됨ㅇㅇ
"env": { "node": true, "vue/setup-compiler-macros": true },
defindProps 는 <script setup> 내에서만 사용할 수 있는 컴파일 타임 매크로이고 템플릿에 선언된 props 는 자동으로 노출됨 (컴파일러 매크로이기 때문에 개발 환경 설정에 따라 lint 에러나 경고가 나올수 있음)
그리고 defineProps 는 컴포넌트에 전달된 모든 props 를 객체로 반환하므로, 필요한 경우 Js에서 접근 가능
<script setup>//BlogPost.vue // defineProps(['title']) const props = defineProps(['title']); console.log(props.title); </script> <template> <h4>{{ title }}</h4> </template> <style scoped> </style>
<script setup> 을 사용하지 않을 때
export default { props: ['title'], setup(props) { console.log(props.title) } }
컴포넌트는 원하는 만큼 props 를 가질 수 있으며, 기본적으로 모든 값을 모두 props에 전달 가능
props 가 등록되면 아래처럼 속성 전달 가능
<BlogPost title="Vue와 함께한 나의 여행"/> <BlogPost title="Vue를 케이크처럼 쉽게 먹는 방법"/> <BlogPost title="Vue와 함께 소주한잔~"/>
배열로 변경
일반적으로는 부모 컴포넌트에 배열이 있음,ㅇㅇ 여기선 App.vue
const posts = ref([ { id: 1, title: 'Vue와 함께한 나의 여행'}, { id: 2, title: 'Vue를 케이크처럼 쉽게 먹는 방법'}, { id: 3, title: 'Vue와 함께 소주한잔~'}, ]) //template <BlogPost v-for="post in posts" :key="post.id" :title="post.title" />
* v-bind 구문 ( :title="post.title") 이 동적 props 값을 전달하는데 사용되는 것에 주목, 미리 정확히 렌더링할 내용을 모를 때 유용
props 가이드
https://ko.vuejs.org/guide/components/props.html
Vue.js
Vue.js - The Progressive JavaScript Framework
vuejs.org
이벤트 청취하기
<BlogPost> 컴포넌트를 개발할 때 일부 기능은 상위 항목과 다시 통신해야 할 수 있음
예를 들어, 페이지의 나머지 부분은 기본 크기로 유지하면서, 블로그 게시물의 텍스트를 확대하는 접근성 기능을 포함하도록 하겠음
const posts = ref([ /* ... */ ]) const postFontSize = ref(1)//App.vue
템플릿에서 모든 블로그 게시물의 글꼴 크기 제어 (fontSize <<<<<<<<주의 대문자S임;)
<div :style="{ fontSize: postFontSize + 'em' }"> <BlogPost v-for="post in posts" :key="post.id" :title="post.title" /> </div>
버튼추가
<!-- BlogPost.vue의 <script> 생략 --> <template> <div class="blog-post"> <h4>{{ title }}</h4> <button>텍스트 확대</button> </div> </template> //BlogPost.vue
버튼에 기능 추가(부모 컴포넌트에 이벤트수신 설정 App.vue)
<BlogPost ... @enlarge-text="postFontSize += 0.1" />
그 다음 자식 컴포넌트는 빌트인 $emit 메서드를 호출, 이벤트 이름을 전달하여 자체적으로 이벤트 생성
<!-- BlogPost.vue의 <script> 생략 --> <template> <div class="blog-post"> <h4>{{ title }}</h4> <button @click="$emit('enlarge-text')">텍스트 확대</button> </div> </template>
@enlarge-text="postFontSize += 0.1"
리스너 덕분에 부모 컴포넌트는 이벤트를 수신하고 postFontSize 값을 업데이트 'enlarge-text' 는 자체생성임 변수느낌
defineEmits 매크로를 사용하여 원하는 이벤트를 선언 가능
<!-- BlogPost.vue --> <script setup> defineProps(['title']) defineEmits(['enlarge-text']) </script>
defineEmits 는 컴포넌트가 내보내는 모든 이벤트를 문서화하고 선택적으로 유효성 검사를 함
또 Vue가 자식 컴포넌트의 루트 엘리먼트에 암시적으로 네이티브 리스너가 적용되는 것을 방지할 수 있음
defineProps 와 마찬가지로 defineEmits 도 <script setup> 에서만 쓸 수 있고 import 할 필요 X
$emit 메서드와 동일한 emit 함수를 반환하므로, 컴포넌트의 <script setup> 섹션에서 이벤트를 내보내는데 사용할 수 있음
* 컴포넌트 emit 타입 지정하기
https://ko.vuejs.org/guide/typescript/composition-api.html#typing-component-emits
Vue.js
Vue.js - The Progressive JavaScript Framework
vuejs.org
<script setup> 을 사용하지 않을 때
export default { emits: ['enlarge-text'], setup(props, ctx) { ctx.emit('enlarge-text') } }
*사용자 정의 이벤트
https://ko.vuejs.org/guide/components/events.html
Vue.js
Vue.js - The Progressive JavaScript Framework
vuejs.org
슬롯이 있는 컨텐츠 배포
부모컴포넌트에서 자식컴포넌트로 텍스트 이동
<AlertBox> 나쁜 일이 일어났습니다. </AlertBox>
<!-- AlertBox.vue --> <template> <div class="alert-box"> <strong>이것은 데모용 에러입니다.</strong> <slot /> </div> </template> <style scoped> .alert-box { /* ... */ } </style>
slot 자리에 "나쁜일이 일어났습니다" 가 박힌다.
* 슬롯 읽어보는게 좋을듯
https://ko.vuejs.org/guide/components/slots.html
Vue.js
Vue.js - The Progressive JavaScript Framework
vuejs.org
동적 컴포넌트
Vue SFC Playground
play.vuejs.org
예제임
위의 동작은 아래의 코드가 있기에 가능함
<!-- currentTab이 변경되면 컴포넌트가 변경됩니다 --> <component :is="tabs[currentTab]"></component>
:is 에 전달된 값은 다음 중 하나를 포함할 수 있음
- 등록된 컴포넌트의 이름 문자열
- 실제 가져온 컴포넌트 객체
is 속성을 사용하여 일반 HTML 엘리먼트를 만들 수도 있음
<component :is=". . .">를 사용하여 여러 컴포넌트 간에 전환할 때, 다른 컴포넌트로 전환되면 컴포넌트가 마운트 해제
내장된 <KeepAlive> 컴포넌트를 사용하여 비활성 컴포넌트를 "활성" 상태로 유지하도록 강제할 수 있음
in-DOM 템플릿 파싱 주의 사항
Vue 템플릿을 DOM 에서 직접 작성하는 경우, Vue 는 DOM에서 템플릿 문자열을 검색해야하고 브라우저 기본 HTML 파싱 동작으로 인해 몇 가지 주의사항이 생김
대소문자를 구분하지 않음
HTML 태그와 속성의 이름은 대소문자를 구분하지 않으므로 브라우저는 대문자를 소문자로 해석
즉, DOM 내 템플릿을 사용할 때 PascalCase 컴포넌트 이름과 props의 camelCased 이름 또는 v-on 이벤트 이름은 모두 kebab-case(하이픈으로 구분된) 기반으로 사용해야 함
// JavaScript에서 camelCase const BlogPost = { props: ['postTitle'], emits: ['updatePost'], template: ` <h3>{{ postTitle }}</h3> ` } <!-- HTML에서 kebab-case --> <blog-post post-title="안녕!" @update-post="onUpdatePost"></blog-post>
셀프 테그 닫기 - 명시적인 닫는 태그 사용 <component /> 이거안됨
<my-component></my-component>
엘리먼트 배치 제한
<ul>, <ol>, <table> 및 <select>와 같은 일부 HTML 엘리먼트에는 내부에 표시할 수 있는 엘리먼트에 대한 제한이 있습니다. 또한 <li>, <tr> 및 <option>와 같은 일부 엘리먼트는 특정 다른 엘리먼트 내부에만 사용할 수 있음
<table> <blog-post-row></blog-post-row> </table>
사용자 정의 컴포넌트 <blog-post-row>는 잘못된 컨텐츠로 호이스트(hoisted)되어 렌더링 출력에서 에러를 발생 시킴
<table> <tr is="vue:blog-post-row"></tr> </table>
이렇게 써야함
ㅇㅇ 그냥 템플릿은 직접쓰지말자
728x90반응형'JS F- > Vue' 카테고리의 다른 글
이름이 있는 라우트 (0) 2024.05.31 Vue Router - 동적 라우트 매칭과 파라미터 (0) 2024.05.31 Vue 라우터 (0) 2024.05.30 템플릿 참조 (0) 2024.05.29 감시자 (0) 2024.05.29 생명 주기 훅 (0) 2024.05.28 다음글이 없습니다.이전글이 없습니다.댓글