본문 바로가기
front/vue

[vue3 공식문서 번역]Components.4.Fallthrough Attributes

by juniKang 2022. 5. 3.

어트리뷰트 상속

"fallthrough attribute"는 컴포넌트로 넘어온 어트리뷰트나 v-on 이벤트 리스너이지만, 컴포넌트의 props나 emits로 받도록 명시적으로 선언되지는 않는다. 흔한예는 class, style, id 어트리뷰트 등이다.

 

컴포넌트가 싱글 루트 엘리먼트를 렌더링할 때, fallthrough 어트리뷰트들은 자동적으로 루트 엘리먼트의 어트리뷰트에 추가된다. 예를들면, <MyButton> 컴포넌트는 다음과 같은 템플릿을 가지고 있다:

<!-- template of <MyButton> -->
<button>click me</button>

부모는 이 컴포넌트를 사용한다:

<MyButton class="large" />

마지막으로 렌더링된 돔은 다음과 같다:

<button class="large">click me</button>

class 와 style 머징

자식 컴포넌트의 루트 엘리먼트가 이미 class나 style 어트리뷰트를 가지고 있다면, 자식으로부터 상속된 class나 style값과 합쳐진다. 이전의 <MyButton>의 템플릿을 다음과 같이 바꾼다고 가정한다:

<!-- template of <MyButton> -->
<button class="btn">click me</button>

그럼 마지막으로 렌더링된 돔은 이렇게 될 것이다:

<button class="btn large">click me</button>

 

v-on 리스너 상속

같은 규칙은 v-on 이벤트 리스너에도 적용된다:

<MyButton @click="onClick" />

click 리스너는 <MyButton>의 루트 엘리먼트에 추가될 것이다. 즉, 네이티브 <button> 엘리먼트에 추가된다. 네이티브 <button>이 클릭되면, 부모 컴포넌트의 onClick 메소드를 트리거 할 것이다. 만약 네이티브 <button>이 이미 v-on:clcik 리스너를 가지고 있다면, 둘 다 트리거 된다.

 

중첩 컴포넌트 상속

만약 컴포넌트가 루트 노드로서 다른 컴포넌트를 렌더링 하면, 예를들어, 우리가 <BaseButton>을 루트로 렌더링 하기 위해 <MyButton>을 리팩토링 한다면:

<!-- 다른 컴포넌트를 렌더링하는 <MyButton/> 의 템플릿
<BaseButton />

그러면 <MyButton>이 받은 fallthrough attributes는 자동적으로 <BaseButton>으로 넘어가게 된다(forwarded).

 

기억할 것 :

1. Forwarded attributes(넘어간 어트리뷰트) 는 <MyButton>에 의해 선언된 이벤트의 v-on 리스너나 props로 선언된 모든 어트리뷰트를 포함하지 않는다. 다시말하면, 선언된 props와 리스너는 <MyButton>에 의해 소모된다.

2. Forwarded attributes가 <BaseButton>에서 만약 선언되었으면, props로 받을 수 있다.

 

어트리뷰트 상속을 비활성화 하기

컴포넌트가 자동으로 어트리뷰트를 상속하는걸 원하지 않는다면, 컴포넌트 옵션에 inhritAttrs: false를 설정할 수 있다.

 

<script setup>을사용하는 경우, 일반 <script> 블록으로 분리해서 이 옵션을 사용해야 한다:

<script>
// 옵션 선언을 위해 normal <script>를 사용
export default {
  inheritAttrs: false
}
</script>

<script setup>
// ...setup logic
</script>

어트리뷰트 상속을 비활성화하는 보편적인 시나리오는 어트리뷰트가 루트 노드 외에 다른 엘리먼트로 사용될 때이다. inheritAttrs 옵션을 false로 설정함으로써, fallthrough attributes가 어디에 적용될지 완벽하게 컨트롤할 수 있다.

 

이 fallthrough attributes는 $attrs 템플릿 표현식을 사용해서 직접적으로 접근할 수 있다:

<span>Fallthrough attributes: {{ $attrs }}</span>

$attrs 객체는 컴포넌트의 props나 emits 옵션에 의해 선언되지 않은 모든 어트리뷰트를 포함한다. (즉, class, style, v-on 리스너 등등).

 

몇가지 주의점:

- props와 다르게, fallthrough attributes는 자바스크립트의 오리지널 케이싱(카멜 케이스 같은)을 보존해서, f00-bar와 같은 어트리뷰트에 접근하기 위해서 $attrs['foo-bar']를 사용해야 한다.

- @click과 같은 v-on 이벤트 리스너는 $attrs.onClick 으로 함수로서 객체에 노출된다.

 

이전 예제의 컴포넌트 예제인 <MyButton>을 사용하면, 스타일링 목적으로 실제 <button>엘리먼트를 추가적인 <div>로 묶어야 할 필요가 있을 수 있다:

<div class="btn-wrapper">
  <buton class="btn">click me</button>
</div>

밖에이는 <div>가 아니라, 안에 있는 <button>에 class나 v-on리스너 같은 모든 faltthrough attributes가 적용되기를 원하면, inheritAttrs:false와 v-bind="$attrs"로 이룰수 있다:

<div class="btn-wrapper">
  <button class="btn" v-bind="$attrs">click me</button>
</div>

아규먼트가 없는 v-bind는 타겟 엘리먼트의 어트리뷰트로서 객체의 모든 프로퍼티를 바인딩한다는걸 기억하라.

 

여러개의 루트 노드에서 어트리뷰트 상속

루트 노드를 하나만 가진 컴포넌트와 달리, 여러개의 루트 노드를 가진 컴포넌트는 자동으로 어리트리뷰트 fallthrough 동작을 가지지 않는다. 만약 $attrs가 명시적으로 바운드되지 않았다면 런타임 경고를 표시한다.

<CustomLayout id="custom-layout" @click="changeValue" />

만약 <CustomLayout>이 멀티 루트 템플릿을 따른다면, 경고가 나올것이다. 왜냐면, 뷰는 fallthorugh attributes를 어디에 적용할지 알 수 ㅇ벗기 때문이다:

<header>...</header>
<main>...</main>
<footer>...</footer>

경고는 만약 $attrs를 명시적으로 바인딩하면 사라질것이다:

<header>...</header>
<main v-bind="$attrs">...</main>
<footer>...</footer>

자바스크립트에서 Fallthrough Attributes에 접근하기

필요하다면, useAttrs() API를 사용해서 <script setup>에서 컴포넌트의 fallthrough attrubutes에 접근할 수 있다:

<script setup>
import { useAttrs } from 'vue'

const attrs = useAttrs()
</script>

만약 <script setup>을 사용하지 않으면, attrs는 setup() 컨텍스트의 프로퍼티로서 노출된다:

export default {
  setup(props, ctx) {
    // fallthrough attributes는 ctx.attrs로서 노출된다
    console.log(ctx.attrs)
  }
}

비록 여기있는 attrs 객체가 가장 최근 fallthroguh attributes만을 항상 리플렉트 할지라도, 이건 reactive가 아니다(성능적인 이우로 인해). 이것의 변화를 관찰하기 위해 watchers를 쓸 수 없다. 만약, 반응형이 필요하다면, prop을 사용해라. 그 대신에, 각 업데이트의 최근 attrs에 사이드에픽트를 수행하도록 onUpdate()를 쓸 수는 있다.

댓글