vue.js - 컴포넌트 - Props

3 minute read

컴포넌트 관계

  • 컴포넌트는 부모-자식 관계에서 가장 일반적으로 사용하기 위한 것입니다.
  • 여기서 부모와 자식 컴포넌트는 서로에게 데이터를 전달하거나, 이벤트를 알릴 필요가 있습니다.
  • Vue에서 부모-자식 컴포넌트 관계는 props(아래)와 events(위로)라고 얘기할 수 있습니다.
  • 부모는 props를 통해 자식에게 데이터를 전달하고, 자식은 events를 이용해 부모에게 메시지를 전달합니다.

component relation

Props

  • 모든 컴포넌트 인스턴스는 자체 범위가 있어, 하위 컴포넌트의 템플릿에서 상위 데이터를 직접 참조할 수 없습니다.
  • 데이터는 props옵션을 사용해서만 하위 컴포넌트로 전달 될 수 있습니다.
<child message="안녕하세요!"></child>
<script>
Vue.component('child', {
  // props 정의
  props: ['message'],
  // 데이터와 마찬가지로 prop은 템플릿 내부에서 사용할 수 있으며
  // 인스턴스의 this.message로 사용할 수 있습니다.
  template: '<span></span>'
})
</script>

컴포넌트 예저-code pen

camelCased 와 kebab-case

  • javascript에서 camelCased 컨벤션을 이용해 작성할 경우 HTML의 표현은 조금 다르게 작성해야합니다.
  • camelCased(대문자 사용) prop이름에 해당하는 HTML속성은 대소문자를 구분하지 않기 때문에 kebab-case(하이픈 구분)를 사용해야합니다.
<script>
Vue.component('child', {
  // JavaScript는 camelCase
  props: ['myMessage'],
  template: '<span></span>'
})
</script>
<!-- HTML는 kebab-case -->
<child my-message="안녕하세요!"></child>
  • 위 예제와 같이 myMessage prop에 해당하는 html은 my-message와 같이 작성해야합니다.

동적 Props

  • v-bind를 이용해서 부모의 데이터에 props에 동적으로 바인딩 할 수 있습니다.
<div id="app">
  <input v-model="parentMsg">
  <br>
  <child v-bind:my-message="parentMsg"></child>
  <!--단축구문 <child :my-message="parentMsg"></child> -->
</div>
<script>
new Vue({
  el: '#app',
  data: {
    parentMsg: 'default'
  },
  components: {
    child: {
      props: ['parentMsg'],
      template: '<span> 동적 입력은  입니다. </span>'
    }
  }
})
</script>
  • 객체의 모든 속성을 props로 전달하려면, 인자없이 v-bind:object 를 사용하면 됩니다.
<script>
todo: {
  text: 'Learn Vue',
  isComplete: false
}
</script>
<todo-item v-bind="todo"></todo-item>
<!-- 똑같은 구문입니다. -->
<todo-item
  v-bind:text="todo.text"
  v-bind:is-complete="todo.isComplete"
></todo-item>

단방향 데이터 흐름

  • 모든 props는 상위에서 하위로 단방향 바인딩을 형성합니다.
  • 이렇게함으로써 하위 컴포넌트가 실수로 부모의 상태를 변경하는것을 방지할 수 있습니다.
  • prop을 변경시키고 싶은 두가지 경우
    1. 초기값을 전달하는데에만 사용하고 이후에는 지역 변수로서 사용하는 경우
    2. 해당 값을 재가공해서 사용하는 경우
  • 두 가지의 경우 아래와 같은 방법으로 회피할 수 있습니다.
// 1번째 : 전달받은 값을 지역변수로 재정의합니다.
props: ['initialCounter'],
data: function () {
  return { counter: this.initialCounter }
}
// 2번째 : 전달받은 값을 계산된 속성에 정의합니다.
props: ['size'],
computed: {
  normalizedSize: function () {
    return this.size.trim().toLowerCase()
  }
}

Prop 데이터 검증

  • props는 받는 데이터형을 지정해 두는 것이 권장되며, 스타일 가이드로는 필수로 지정하고 있습니다.
  • 만약, 지정한 데이터형 이외에 다른 것을 넣으면 경고가 발생하여, 다른 사용자가 컴포넌트를 사용할 때 잘못 사용하는 오류를 방지할 수 있습니다.
Vue.component('example', {
  props: {
    // 기본 타입 확인 (`null` 은 어떤 타입이든 가능하다는 뜻입니다)
    propA: Number,
    // 여러개의 가능한 타입
    propB: [String, Number],
    // 문자열이며 꼭 필요합니다
    propC: {
      type: String,
      required: true
    },
    // 숫자이며 기본 값을 가집니다
    propD: {
      type: Number,
      default: 100
    },
    // 객체/배열의 기본값은 팩토리 함수에서 반환 되어야 합니다.
    propE: {
      type: Object,
      default: function () {
        return { message: 'hello' }
      }
    },
    // 사용자 정의 유효성 검사 가능
    propF: {
      validator: function (value) {
        return value > 10
      }
    }
  }
})
  • type으로 사용가능한 네이트브 생성자는 다음과 같습니다.
    • String
    • Number
    • Boolean
    • Function
    • Object
    • Array
    • Symbol
  • type은 커스텀 생성자 함수가 될 수 있고, 이때 검증은 instanceof로 확인합니다.
  • props의 검증은 컴포넌트 인스턴스가 생성되기 전에 검증되기 때문에 default 또는 validator 함수 내에서 data, computed, methods와 같은 인스턴스 속성을 사용할 수 없습니다.(만들어지지 않았기 때문에)

Props에 정의되지 않은 속성

  • 정의되지 않은 props는 attribute로 전달되었지만, 컴포넌트에 Props로 정의되지 않은 속성을 이야기합니다.
  • 정의된 props는 하위 컴포넌트에 정보를 전달하는데 적합하지만, 다른 컴포넌트 라이브러리의 모든 경우를 예측할 수 없습니다.
  • 이때 정의되지 않은 props를 사용하면 컴포넌트 루트 엘리먼트에 추가되어 다른 컴포넌트 라이브러리에서도 사용할 수 있습니다.
<script>
//bs-date-input컴포넌트 템플릿
Vue.component('bs-date-input', {
  template: '<input type="date" class="form-control">'
})
</script>
<bs-date-input data-3d-date-picker="true"></bs-date-input>
<!-- 아래와 같은 html로 렌더링합니다 -->
<input type="date" class="form-control" data-3d-date-picker="true">
  • 만약 상위에 지정한 속성이 하위에도 존재하는 경우, 보통은 상위에서 전달된 속성으로 교체 또는 병합(class, style)됩니다.
  • 예를들어 아래와 같이 지정할 경우
<bs-date-input
  type="large"
  data-3d-date-picker="true"
  class="date-picker-theme-dark"
></bs-date-input>
<!-- 아래와 같이 변경됩니다. -->
<input type="large" class="date-picker-theme-dark form-control" data-3d-date-picker="true">
  • 만약 부모 컴포넌트에서 전달받은 attribtue를 사용하고 싶지 않을때는 inheritAttrs:false 을 지정하면 자식 컴포넌트의 attribute가 설정되지 않습니다. (기본값은 true입니다.)
  • 아래와 같이 사용할 수 있습니다.
Vue.component('bs-date-input', {
  inheritAttrs: false,
  template: '<input type="date" class="form-control">'
})
  • inheritAttrs와 $attrs를 이용하면 선택적으로 필요한 속성만 자식 컴포넌트에 설정할 수 있습니다.

ps. 찾아보고 공부하면서 정리하는 내용이라 틀릴 수 있음을 알려드립니다.

참고