처음부터 시작하는 Java
  • 컴포넌트 기초
    2024년 05월 29일 18시 50분 37초에 업로드 된 글입니다.
    작성자: 원2
    728x90
    반응형

    컴포넌트를 사용하면 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

     


    동적 컴포넌트

    https://play.vuejs.org/#eNqNVdFu2jAU/RUre6CVCLRrtYcMqnVTpW0PW7X1rZmm4NyAS2JHtsOoEP++Y5uEoJYKkJB97/HxOTc3l010W9ejVUNREk0M16K2zJBt6ptUiqpW2rKvqiJWaFWxwWjsNg4+6NL3yljT5f3uEHCr+UKs9hS7/SFowzQVbLsDhRRLJVfSWMYbrUnah2zGpg53NnAyBuepbBE2mxnkNjjk9Q7dwmvxq92VqdymcjIONmEQG0tVXWaWsGNskosV42VmzDSNcqpUGvk4MrPGWoUDboPPKi6UBujs79Ddfc6E9BrSqIMkS3oGAtF+sGV/HCARB9bBEPYzbqEwOfA6nTpStv3TY/jES8GXYDgoSv+WnWTGNptwHKa9h3G4zhm32HKF0ktQsEQ4SU7/454Vl3a18PQ3k3F3JJRrjHphNRn3qoitsc+lW45cCcMzKZS0cZFVonxOmMmkiQ1pUXx0uZnSOemEXdZrZlQpcvaOiHqpWGe5aEzC3tdrH66zPBdyjsAFzlzhx4erTM+FjK2qQUZVPzZTcF4l7LrFNrgfGkriNmESnnxUrUgXpfoXrxOWNVYhiOLByf5hBT+dgg8QcNmS7uRCQFxSYTvdVy/zWswXxwAHpeCc+xQejFFI1UpISzrAM76ca9XIPGHvigv3fc10DLp+3N/dhWGwZy9ZuBoEkwf0dOG+L/Cj0LcnHNhBjjvsarqrZ3hXfS9Fw6gdPG5OHX9pQ6OGfvdzq+vYIw0L5m5knU4dZt4J3L1Zdzp7OzBP4LcG868Q89GTURI3+BpjNuCkKEn/rK3AfEyjJFTf5bISDf7dx6xuwqT0ZxbEl6/En8zaxdLoXhNeGkzRqMtZNBTZkL77/YPWWHfJSuVNCfQbyV+EHmicxgD7jNaB7B7Oq/3m/yPQGw/mbm1JmtaUE+qQW49PI5T5yxvW93KvRtf+HLos2v4HdCJYbw==

     

    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
    댓글