FrontEnd

vue js 내용정리 (2.3.9 컴포넌트-slot태그 ~ 2.6. 사용자 정의 이벤트)

Dean83 2022. 3. 25. 09:24

   2.3.9. <slot> 태그 
                      - 컴포넌트 template 에 slot 태그를 사용시, slot 태그를 부모가 원하는 내용으로 교체하여 채워넣을 수 있음. 
                      - <컴포넌트명></컴포넌트명> 사이에 원하는 html 태그를 입력하면, slot 태그를 대체하게 됨
                      - 하나의 컴포넌트에 공통기능만 명시하고 여러군데서 커스터마이징 하여 사용할 수 있음

Vue.component('test',
{
     ...
     template:'<div> <h2>나는 자식 컴포넌트의 제목입니다</h2> <slot> 제공된 컨텐츠가 없는 경우에만 보실 수 있습니다. </slot> </div>'
}
)

html에서
<test><p>안녕하세요</p><p>안녕하세요1</p></test>

=> <slot> 태그를 <p>안녕하세요.... 가 대체하게 됨.
=> 최종적으로, <div> <h2>나는 자식 컴포넌트의 제목입니다</h2> <p>안녕하세요</p><p>안녕하세요1</p></div>
=> <test> 태그 사이에 아무것도 없을경우 slot의 내용이 보임

                      2.3.9.1. <slot> 태그 name 속성
                                   - 컴포넌트 template에 slot 태그가 여러개 있을 경우.
                                   - 부모 html태그가 대체될 slot 위치를 지정.
                                   - 컴포넌트 template에서 <slot name="이름"> 으로 선언
                                   - 부모 html에서 <태그 slot="이름" ... 으로 사용

Vue.compnent('test',
{
   template='
          <div class="container">
          <header>
             <slot name="header"></slot>
           </header>
         <main>
           <slot></slot>
         </main>
         <footer>
             <slot name="footer"></slot>
         </footer>'
</div>
}

부모 html에서,
<test>
<p slot="header">헤더</p> 
<p>안녕하세요</p> 
<p>안녕하세요1</p> 
<p slot="footer">가나다라</p>
</test>

결과
<div class="container">
  <header>
    <p>헤더</p>
  </header>
  <main>
    <p>안녕하세요</p>
    <p>안녕하세요1</p>
  </main>
  <footer>
    <p>가나다라</p>
  </footer>
</div>

                      2.3.9.2. <slot> 태그 scope 속성
                                   - 컴포넌트 template slot태그에 데이터를 바인딩하여 사용시, 부모 html에서 자식 데이터를 접근하기 위해 사용함
                                   - 부모 html에서 <template> 태그를 사용했어야 하나, 최신버전에서는 이에 상관없이 기본 태그에서도 사용 가능
                                   - <태그명 slot-scope="원하는 이름"....> {{원하는이름.컴포넌트slot 속성명.맴버변수명 }} ... 식으로 사용 가능
                                   - 예

Vue.component('test',
{             
   template:'<ul>
              <li
                v-for="todo in todos"
                v-bind:key="todo.id"
              >
                <slot v-bind:todoItem="todo">
                  {{ todoItem.text }}
                </slot>
              </li>
            </ul>'
   data: function()
   {
    return todos: [ { id: 1, text: "일번"}, { id: 2, name: "이번"}, { id: 3, name: "삼번"} ]
   }
})

new Vue({

el:'#instanceID',
data : { ListItem:[........] }

})

html에서
<div id="instanceID">
<test v-bind="ListItem">
  <template slot-scope="Items(원하는이름)"> //template 대신 다른 일반태그 사용가능
    <span v-if="Items(원하는이름).todoItem.isComplete">✓</span>
    {{ Items(원하는이름).todoItem.text }}
  </template>
</test>  
</div>

                                   2.3.9.2.1. 디스트럭쳐링
                                                   - html에서 slot-scope 사용을 좀 더 편리하게 사용하기 위한 방법
                                                   - 기존 : 원하는이름.컴포넌트slot 속성명.맴버변수
                                                   - 적용 :  slot-scope={컴포넌트slot 속성명} 이후, 컴포넌트slot 속성명.맴버변수 로 사용

<todo-list>
  <template slot-scope="{ todo }">
    <span v-if="todo.isComplete">✓</span>
    {{ todo.text }}
  </template>
</todo-list>

            2.3.10. 동적 컴포넌트 (내부 컴포넌트)
                      - <component> 태그 및 v-bind:is="" 를 이용하여, 렌더링할 컴포넌트를 동적으로 바인드

//html 코드
<component v-bind:is="currentView">
  <!-- vm.currentView가 변경되면 컴포넌트가 변경됩니다! -->
</component>

//모델
var Home = {
  template: '<p>Welcome home!</p>'
}

//vue 인스턴스 
var vm = new Vue({
  el: '#example',
  data: {
    currentView: Home
  }
})

================= 내부 컴포넌트 사용

var vm = new Vue({
  el: '#example',
  data: {
    currentView: 'home'
  },
  components: {
    home: { '<p>Welcome home!</p>' },
    posts: { '<p>Welcome post!</p>' },
    archive: { '<p>Welcome archive!</p>' }
  }
})

                      2.3.10.1. keep-alive
                                     - <component> 태그 및 v-bind:is="" 를 이용하여 동적으로 생성한 컴포넌트를 메모리에 유지
                                     - <keep-alive> 태그 안에 <component> 태그를 사용

            2.3.11. 비동기 컴포넌트 (이해가...)
                        - 여러 방법이 있음....
                        2.3.11.1. 컴포넌트 생성시, 생성자에 function 사용 (factory 함수)
                                       - 함수 인자값으로 resolve, reject 가 있음.
                                       - Vue.component('컴포넌트명', function(resolve, reject) { ... }) 

                                        2.3.11.1.1. resolve 콜백
                                                         - 콜백으로, 컴포넌트 가져오기 성공시 컴포넌트를 렌더링 하기 위함

Vue.component('async-example', function (resolve, reject) {
    setTimeout(function () {
    resolve({
      template: '<div>I am async!</div>'
    })
   },1000)
})

//settimeout은 javascript -> windows 에서 제공하는 함수https://www.w3schools.com/jsref/met_win_settimeout.asp 참조

                                        2.3.11.1.2. reject 콜백
                                                         - 콜백으로, 컴포넌트 가져오기 실패시 컴포넌트를 렌더링 하기 위함

Vue.component('async-example', function (resolve, reject) {
    reject({
      template: '<div>fail</div>'
    })
})

                        2.3.11.2. webpack과 함께 사용
                                         - webpack과 사용시, 각 컴포넌트 들은 별도의 파일로 저장하여 존재하는것을 추천.
                                         - import 하여 사용

Vue.component('async-webpack-example', function (resolve) {
  // 이 특별한 require 구문은 Webpack이 Ajax 요청을 통해
  // 로드되는 번들로 작성된 코드를 자동으로 분리하도록 지시합니다.
  require(['./my-async-component'], resolve)
})

                        2.3.11.3. webpack2 + ES2015구문
                                         - webpack과 사용시, 각 컴포넌트 들은 별도의 파일로 저장하여 존재하는것을 추천.
                                         - import 하여 사용

Vue.component(
  'async-webpack-example',
  // `import` 함수는 `Promise`를 반환합니다.
  () => import('./my-async-component')
)

//promise는 자바스크립트의 비동기에서 사용되는 객체

                        2.3.11.4. 고급 비동기 컴포넌트
                                       - const 를 사용
                                       - component: import('컴포넌트 위치..??'),  또는 new promise ((resolve, reject) => { ... }) 
                                          - 로드하는 컴포넌트. 반드시 promise (자바스크립트 비동기에서 사용되는 객체) 이어야 함 
                                          - import : https://developers.google.com/web/updates/2017/11/dynamic-import
                                          - import를 사용하려면, 각 컴포넌트들이 별도의 파일로 저장되어야 함
                                       - loading: const 로 선언한 함수명??
                                          - 비동기 컴포넌트가 로드되는동안 사용
                                       - error: const 로 선언한 함수명??
                                          - 실패했을경우 사용
                                       - delay:숫자
                                          - 로딩 함수?? 를 보여주기전 지연하는 정도. ms
                                       - timeout:숫자
                                          - 이 시간이 초과되면 error:에서 설정한 함수?? 실행됨 

html 부분
<div id="app">
    <async-component></async-component>
</div>

<template id="number-box">
  <h1>123123</h1>
</template>

<template id="loading-component">
  <h1>Loading...</h1>
</template>

<template id="error-component">
  <h1>Something Wrong!!</h1>
</template>

스크립트 부분
const NumberBoxComponent = {
    name: 'number-box',
    template: '#number-box',
}

const LoadingComponent = {
    name: 'loading-component',
    template: '#loading-component'
}

const ErrorComponent = {
    name: 'error-component',
    template: '#error-component'
}

const AsyncComponent = () => ({
    component: new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve(NumberBoxComponent)
        }, 1000)
  }),
  loading: LoadingComponent,
  error: ErrorComponent,
  delay: 1
})

new Vue({
    el: '#app',
  components: {
      'async-component': AsyncComponent
    }
})
})

            2.3.12. 재귀 컴포넌트
                        - 컴포넌트 등록시 설정한 name을 통해 template 에서 재귀 호출을 할 수 있음. 
                        - 전역 등록을 할 경우, Vue.component('이름입니다', { ... }) 첫번째 인자가 name임.
                        - 지역등록일 경우, name:'명칭' 옵션을 이용하여 이름 등록
                        - 무한루프 가능성이 있으므로, v-if를 통해 제어 해주어야 함

Vue.component('unique-name-of-my-component', {
  ...
  template:'<unique-name-of-my-component></unique-name-of-my-component>....'
})

// 무한루프가 발생함. template를 아래와 같이 변경
// template: '<unique-name-of-my-component v-if="변수명 < 숫자" v-bind:변수명="변수명 + 1"></unique-name-of-my-component>....'

2.4. 이벤트 핸들링
           - v-on 디렉티브 이용 (2.2.2.11. 참조)
           2.4.1. 변수값 직접 변경
                     - v-on:click="counter += 1" 처럼 직접 변수값을 변경가능
           2.4.2. 함수 호출
                     - v-on:click="함수명" 으로 함수 호출도 가능
           2.4.3. 인자값이 있는 함수 호출 (인라인 메소드)
                     - 함수명이 test(message) 인 함수를 호출
                        - v-on:click="test('hello')" 으로 함수 호출
           2.4.4. 원본 변수(?) 전달
                     - 함수명이 warn(message,event) 인 경우
                       - v-on:click="warn('hello',$event)" 으로 호출
           2.4.5. 이벤트 수식어
                     - 2.4.4. 에서 클릭시 $event를  전달할 경우, 함수 내부에서 event에 속한 메서드 호출 등이 가능하다. 
                     - 그러나, 해당 이벤트의 특정 상황에서만 동작을 원할 경우, 수식어를 사용할 수 있다. 
                     - v-on:이벤트명.수식어="" 으로 사용
                     - 수식어는 좌측->우측으로 우선순위가 있으므로 순서를 따라야 함
                        - click.prevent.self : 모든 클릭 차단, clck.self.prevent : 이 엘리먼트의 클릭만 차단
                     - 예

<!-- 클릭 이벤트 전파가 중단됩니다 -->
<a v-on:click.stop="doThis"></a>

<!-- 제출 이벤트가 페이지를 다시 로드 하지 않습니다 -->
<form v-on:submit.prevent="onSubmit"></form>

<!-- 수식어는 체이닝 가능합니다 -->
<a v-on:click.stop.prevent="doThat"></a>

<!-- 단순히 수식어만 사용할 수 있습니다 -->
<form v-on:submit.prevent></form>

<!-- 이벤트 리스너를 추가할 때 캡처모드를 사용합니다 -->
<!-- 즉, 내부 엘리먼트를 대상으로 하는 이벤트가 해당 엘리먼트에서 처리되기 전에 여기서 처리합니다. -->
<div v-on:click.capture="doThis">...</div>



<!-- event.target이 엘리먼트 자체인 경우에만 트리거를 처리합니다 -->
<!-- 자식 엘리먼트에서는 안됩니다 -->
<div v-on:click.self="doThat">...</div>

// 한번만 트리거 됨
<a v-on:click.once="doThis"></a>

등등 존재하기에 추후 구글링 필요.

           2.4.6. 키 수식어
                     - 키보드 이벤트 수신시, 키 코드 확인 필요할때 사용 
                     - v-on:keyup.enter ="콜백함수명"  => 엔터키 클릭시 동작
                     - 키 수식어 전체 목록
                       - .enter
                       - .tab
                       - .delete (delete 키와 backspace 모두 캡쳐됨)
                       - .esc
                       - .space
                       - .up
                       - .down
                       - .left
                       - .right
                       - .ctrl
                       - .alt
                       - .shift
                       - .meta

                    2.4.6.1. 시스템 키
                                 - ctrl, alt 등 시스템 키가 눌렸을 경우에 동작하고 싶을때 조합하여 사용
                                 - click.ctrl : 컨트롤키가 눌린상태에서 클릭했을 경우.
                                 - keyup.alt.enter : 알트키가 눌린상태에서 엔터키 눌렀을경우
                                 - .ctrl, .alt, .shift, .meta (맥os의 커맨드키)

                    2.4.6.2. exact 수식어
                                 - 정확한 조합을 위한 수식어
                                 - 예

<!-- Alt 또는 Shift와 함께 눌린 경우에도 실행됩니다. -->
<button @click.ctrl="onClick">A</button>

<!-- Ctrl 키만 눌려있을 때만 실행됩니다. -->
<button @click.ctrl.exact="onCtrlClick">A</button>

<!-- 아래 코드는 시스템 키가 눌리지 않은 상태인 경우에만 작동합니다. -->
<button @click.exact="onClick">A</button>

                    2.4.6.3. 마우스 버튼
                                 - .left, .right, .middle

                    2.4.6.4. 커스텀 수식어
                                 - 원하는 문자열과 매칭할 키코드를 연결하여 커스텀 수식어를 사용 할 수 있음
                                 - 참조 : https://kr.vuejs.org/v2/api/#keyCodes

Vue.config.keyCodes = {
  v: 86,
  f1: 112,
  // camelCase는 작동하지 않습니다.
  mediaPlayPause: 179,
  // 쌍따옴표로 감싸진 kebab-case를 사용하세요
  "media-play-pause" : 179,
  up: [38, 87]

  //원하는수식어명:정확한 키코드번호
  // 키코드 여러개를 연결할 경우 [] 사용
}

    2.5. 폼 입력 바인딩
           - v-model 사용
           - 폼 입력과 양방향으로 바인딩
           - v-model은 폼의 기본값 (value, checked, selected 같은)을 무시하고 Vue 인스턴스의 값을 원본으로 사용함. 따라서 초기화는 Vue 인스턴스에서 해야 함. 
           - 폼 입력과 양방향으로 바인딩
           - <태그명 v-model="연동변수명" ...

           2.5.1. input 태그
                    - input은 닫는 태그가 없음
                    - 예                    

<input v-model="message" placeholder="여기를 수정해보세요">
<p>메시지: {{ message }}</p>

           2.5.2. textarea
                     - 다중 열을 가지는 텍스트 입력
                     - 예

<span>여러 줄을 가지는 메시지:</span>
<p style="white-space: pre-line">{{ message }}</p>
<br>
<textarea v-model="message" placeholder="여러줄을 입력해보세요"></textarea>

           2.5.3. 체크박스
                     - 여러개의 체크박스는 같은 배열을 바인딩 할 수 있음
                     - 하나의 체크박스는 bool 값과 연동됨
                     - 예 

<div id='example-3'>
  <input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
  <label for="jack">Jack</label>
  <input type="checkbox" id="john" value="John" v-model="checkedNames">
  <label for="john">John</label>
  <input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
  <label for="mike">Mike</label>
  <br>
  <span>체크한 이름: {{ checkedNames }}</span>
</div>

new Vue({
  el: '#example-3',
  data: {
    checkedNames: []
  }
})

// label 의 for 어트리뷰트에 폼의 id를 넣어주면, label과 폼이 연결됨
// 이 경우, 체크했을경우 해당 연결된 라벨이 보여짐
// input의 value가 checkdNames 배열의 값이며, 
// 화면에는 label에서 설정한 문자열이 보여짐 (mike....)

           2.5.4. 라디오 버튼
                     - 선택한 항목의 value 값이 설정됨
                     - 예

<input type="radio" id="one" value="One" v-model="picked">
<label for="one">One</label>
<br>
<input type="radio" id="two" value="Two" v-model="picked">
<label for="two">Two</label>
<br>
<span>선택: {{ picked }}</span>

// picked 변수에 input value인 One, Two 가 배정됨. 선택된 항목의 값이 배정됨

           2.5.5. 컴보박스 (html에서는 select 라고 부름)
                     - option 태그에 설정된 값이 변수에 바인딩됨.
                     - 예

<select v-model="selected">
  <option disabled value="">Please select one</option>
  <option>A</option>
  <option>B</option>
  <option>C</option>
</select>
<span>선택함: {{ selected }}</span>

new Vue({
  el: '...',
  data: {
    selected: ''
  }
})

// disabled 를 통해 선택이 되지 않게 함.
// value 속성을 사용치 않는경우, 태그 사이의 값이 (A or B or C) value가 됨
// selected는 기본선택이나, v-model을 쓰는경우 무시함

           2.5.6. 리스트박스 (html에서는 다중 select 라고 부름)
                     - option 태그에 설정된 값이 변수에 바인딩됨
                     - 쉬프트를 누른상태에서 다중선택 됨

<select v-model="selected" multiple>
  <option>A</option>
  <option>B</option>
  <option>C</option>
</select>
<br>
<span>Selected: {{ selected }}</span>

                     2.5.6.1. v-for를 이용한 동적 리스트 박스
                                  - 예  

<select v-model="selected">
  <option v-for="option in options" v-bind:value="option.value">
    {{ option.text }}
  </option>
</select>
<span>Selected: {{ selected }}</span>

new Vue({
  el: '...',
  data: {
    selected: 'A',
    options: [
      { text: 'One', value: 'A' },
      { text: 'Two', value: 'B' },
      { text: 'Three', value: 'C' }
    ]
  }
})

// v-for를 이용해 동적으로 option태그를 생성.

           2.5.7. 오브젝트 바인딩
                     - 위의 방법으로 바인딩시, 문자열 혹은 bool 값이 바인딩 됨.
                     - 경우에 따라선 맴버변수 등 오브젝트가 바인딩 되어야 할 때가 있음.
                     - 이를위해 value 에 v-bind를 사용하여 Vue 인스턴스의 맴버변수를 지정할 수 있음
                     - v-model 에 v-bind에서 지정한 오브젝트가 바인딩 되게 됨. 

                     2.5.7.1. 라디오 버튼
                                  - 예

<input type="radio" v-model="pick" v-bind:value="a">

체크시, vm(Vue Instance).pick 에 vm.a(vue instance의 a변수) 가 담긴다.

                     2.5.7.2. List, 컴보박스 (Select 태그)
                                  - 예

<select v-model="selected">
  <!-- inline object literal -->
  <option v-bind:value="{ number: 123 }">123</option>
</select>

선택시, vm.selected 를 접근하면, object가 담겨있음. 
이 경우 vm.selected.number 에 123이 들어가게 됨. 
즉, selected의 맴버변수에 값을 할당한것임.

           2.5.8. 수식어
                     - v-model.수식어 로 사용

                     2.5.8.1. .lazy
                                  - change 이벤트 이후에 동기화 됨. 
                     2.5.8.2. .number
                                   - 입력값을 숫자로 변환
                                   - input 폼에서 사용하며, type를 number로 지정해도 문자열 입력이므로, 이 옵션을 사용해야 숫자가 됨
                     2.5.8.3. .trim
                                   - 입력 문자열에 trim 적용

    2.6. 사용자 정의 이벤트
           - 참조 : 
https://kr.vuejs.org/v2/guide/components.html#v-on%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EC%82%AC%EC%9A%A9%EC%9E%90-%EC%A7%80%EC%A0%95-%EC%9D%B4%EB%B2%A4%ED%8A%B8
           - html 태그의 속성에서 v-on 을 사용하여 특정이벤트 발생시 수행할 작업을 지정할 수 있음
           - html에서 사용자 생성 이벤트 또한, v-on을 통해 감지 할 수 있음
             - 단, 사용자 이벤트는 $on으로 감지할 수 없음             

<button-counter v-on:increment="incrementTotal"></button-counter>

// button-counter 컴포넌트 템플릿 버튼 클릭시, incrementCounter 함수 호출 -> emit을 통한 이벤트 발생
// -> 위의 html에서 작성한 button-counter에서 v-on으로 사용자 정의 이벤트인 increment를 감지

Vue.component('button-counter', {
  template: '<button v-on:click="incrementCounter">{{ counter }}</button>',
  data: function () {
    return {
      counter: 0
    }
  },
  methods: {
    incrementCounter: function () {
      this.counter += 1
      this.$emit('increment')
    }
  },
})

new Vue({
  el: '#counter-event-example',
  data: {
    total: 0
  },
  methods: {
    incrementTotal: function () {
      this.total += 1
    }
  }
})

           2.6.1. ref
                     - html 태그를 코드에서 접근하여 사용하기 위해 reference 하는것.
                     - html 태그에서 ref 속성을 사용
                       - <태그명 ref="원하는 문자열">
                     - 코드에서 $refs.원하는문자열 로 접근하여 사용

<button ref="myButton" @click="clickedButton">Click Me!</button>

this.$refs.myButton.innerText = this.message;

           2.6.2. on('이벤트명',콜백함수)
                     - 이벤트를 수신

vm.$on('test', function (msg) {
  console.log(msg)
})

           2.6.3. emit('이벤트명',인자값1,......)
                     - 이벤트 발생

this.$emit('이벤트명', 'hello')