Non-Prop 속성은 props 또는 event 에 명시적으로 선언되지 않은 속성 또는 이벤트이다. (ex. class, style, id)
이전포스팅(emits)에서 '왜 이벤트를 선언해야 하는가?'라는 질문에 'vue가 non-prop 속성에 알려진 리스너(Fallthrough)를 제외할 수 있기 때문'이라고 답한적이 있다.
이번 포스팅은 저 문장을 이해하는 포스팅이 될 것이다.
결론은 간단해서 포스팅을 망설였으나, 'why'에 집중하기에 포스팅을 작성해본다.
먼저, 당연하듯 예시부터 살펴보겠다.
LabelInput.vue
라는 자식 컴포넌트가 있고, 이를 부모 컴포넌트에서 다음과 같이 사용했다고 가정해보자.
<!--부모 컴포넌트-->
<LabelInput label="이름" class="non-class" style="color: red">
자식 컴포넌트의 이벤트 선언 부분을 살펴보자.
export default{
props:['label']
}
label
은 props에 선언되어 있지만, class
와 style
은 props에 선언되어 있지 않다. 이때 선언되어 있지 않은 class
와 style
같은 속성 또는 이벤트를 Non-prop 속성이라 한다.
그럼 이 속성들은 어떻게 될까?
루트 요소의 속성에 자동으로 추가된다. 속성 상속이 이루어지는 것이다.
Non-prop 속성은 루트요소에 자동으로 추가된다.
만약, 자식 컴포넌트 루트요소에 이미 class
와 style
속성이 정의되어 있으면, 부모로부터 받은 class
와 style
속성과 병합한다.
이 외는 덮어씌운다.
<labelInput @click="onClick">
@click
리스너는 <labelInput>
컴포넌트 루트요소에 추가된다.
만약 루트요소에 이미 바인딩된 이벤트가 있다면 두 리스너 모두 트리거 된다.
여기서 주의해야할 점은, 루트요소에 추가된다는 것이다.
루트요소에 이벤트가 추가되면 의도치 않은 상황이 발생할 수 있다.
예를 들어, 자식 컴포넌트 labelInput.vue가 다음과 같이 작성되어 있다고 가정해보자.
<template>
<div class="bg-danger">
<button type="button">버튼</button>
</div>
</template>
부모컴포넌트에서 상속받은 non-props 이벤트리스너 @click="onClick"
은 루트요소인 <div class="bg-danger">
에 상속될 것이다.
즉, 나는 버튼을 클릭했을 때만 이벤트를 실행시키고 싶은데 <div>
영역을 클릭하면 이벤트가 실행되는 현상이 나타나는 것이다.
어떻게 해결해야할까? 이때 필요한것이 속성 상속의 비활성화이다.
컴포넌트가 자동으로 Non-Prop 속성을 상속하지 않도록 하기위해 컴포넌트의 inheritAttrs: false 옵션을 설정할 수 있다.
<script>
export default {
inheritAttrs: false,
};
</script>
컴포넌트에 Non-Prop 속성을 비활성화 하는 일반적인 경우는 자식 컴포넌트의 루트요소에 이외의 다른 요소에 Non-Prop 속성을 적용하고 싶을 때이다.
그리고 적용해야하는 요소에 <template>
에서 Non-Prop속성에 접근할 수 있는 내장 객체 $attrs
로 직접 접근할 수 있다.
$attrs
객체에는 컴포넌트에 선언되지 않은 모든 속성 props, emits (ex. class, style, v-on 등)을 포함하고 있다.
<template>
<div>
<button v-bind="$attrs">버튼</button>
</div>
</template>
스크립트 셋업에서는 context로 접근할 수 있다.
<script>
export default(){
inheritAttrs: false,
setup(props, context){
console.log(context.attrs);
console.log(context.attrs.class);
console.log(context.attrs.onClick);
}
}
</script>
💡 주의사항
props와 달리 Non-Prop 속성은 JavaScript에서 원래 대소문자를 유지하므로 foo-bar와 같은 속성은 대괄호를 사용하여 $attrs[’foo-bar’]
로 접근해야 한다.
@click과 같은 v-on리스너는 $attrs.onClick
과 같이 함수로 접근할 수 있다.
Vue3에서는 다중 루트 노드 컴포넌트인 fragments를 공식 지원한다.
vue2에서는 다중 루트 노드가 지원되지 않아서 단일 <div>
로 감싸야했다.
<template>
<div>
<header>...</header>
<main>...</main>
</div>
</template>
하지만, vue3부터는 다중 루트 노드를 가질 수 있다.
다만, 속성을 배포(상속)해야 하는 위치를 명시적으로 정의해야한다. (non-props 속성을 어느 루트에 상속해야 하는지 알지 못하기 때문)
<template>
<header>...</header>
<main v-bind="$attrs">...</main>
<footer>...</footer>
</template>
단일 루트 요소가 있는 컴포넌트와 달리 여러 루트 요소가 있는 컴포넌트에는 자동으로 Non-Prop 속성이 상속되지 않는다.
만약 명시적으로 $attrs
를 바인딩 하지 않을 경우 런타입 경고가 발생된다.
vue3 공식문서 fallthrough
vue3 완벽마스터 - 짐코딩