cover

setup 훅에서 두 번째 매개변수로 context를 받고 있는데요. 그 안에는 attrs, slots, emit, expose 가 들어있습니다. 이 값들을 어떤 용도로 어떻게 사용하는지 하나씩 살펴보겠습니다.

context

setup 훅에서 전달되는 두 번째 매개변수는 context로 4가지 값이 들어있습니다.

attrs: fallthrough attributes가 들어있습니다. ($attrs와 동일)
slots: slot에 넣어진 태그 값들이 들어있습니다. ($slots와 동일)
emit: 상위컴포넌트에 전달할 이벤트를 등록할 수 있습니다. ($emit과 동일)
expose: 상위 컴포넌트에서 사용가능한 변수, 함수 등을 제한할 수 있습니다.
javascript
      export default {
  setup(props, context) {
    console.log(context.attrs)
    console.log(context.slots)
    console.log(context.emit)
    console.log(context.expose)
  }
}
    

script setup에서 가져오는 방법

script setup에서는 setup 훅처럼 context 매개변수를 받을 수 없기 때문에 다른 방식으로 값을 가져옵니다.

attrs : useAttrs()
slots : useSlots()
emit : defineEmits()
expose : defineExpose()
html
      <script setup>
import { ref, useAttrs, useSlots } from 'vue'

const attrs = useAttrs()
const slots = useSlots()
defineEmits(['submit'])

const a = 1
const b = ref(2)
defineExpose({ a, b });
</script>
    

attrs

컴포넌트에서 props와 emits으로 정의해둔 게 아닌 속성이나 이벤트 리스너는 fallthrough attribute라 합니다. class, style 등이 있으며 이 값들은 attrs을 통해 전달됩니다. 이에 대해서는 이미 이전 글에서 다룬 적 있으므로 자세한 내용은 생략하겠습니다.

만약 다음과 같이 컴포넌트에 속성을 전달했다면

html
      <MyComponent class="active" style="font-size: 10px" />
    

attrs 값은 다음과 같이 들어있습니다.

javascript
      export default {
  setup(props, { attrs }) {
    console.log(attrs.class); // active
    console.log(attrs.style); // {'font-size': '10px'}
  },
};
    

주의할 점은 읽기 전용이기 때문에 수정할 수 없습니다.

slots

slots은 render 함수로 렌더링할 경우 사용합니다. 만약 상위 컴포넌트에서 다음과 같이 slot을 추가한다면

html
      <MyComponent>
  <template v-slot:header="slotValue">
    <header>{{ slotValue.text }}</header>
  </template>
  <template v-slot:default>
    <main></main>
  </template>
</MyComponent>
    

컨텍스트의 이 slots 값을 출력하면 어떤 함수 값이 나타나는 것을 볼 수 있습니다.

javascript
      export default {
  setup(props, { slots }) {
    console.log(slots);
  },
};
    
image

이 값들은 vnodes의 배열을 반환하는 함수들입니다. 그래서 사용한다면 렌더 함수랑 같이 사용해야합니다.

html
      <template>
  <div></div>
</template>

<script>
import { h } from "vue";
export default {
  setup(props, { slots }) {
    return () => [
      // named slot:
      // <header><slot name="footer" :text="message" /></header>
      h(
        "header",
        slots.header({
          text: "hello",
        })
      ),
      // default slot:
      // <main><slot /></main>
      h("main", slots.default()),
    ];
  },
};
</script>
    

그러면 다음과 같이 렌더링된 것을 볼 수 있습니다.

image

물론 굳이 이렇게 어렵게 할 필요없이 주석에서처럼 slot 태그를 이용하면 되지만요…

emit

하위 컴포넌트에서 상위 컴포넌트로 전달할 이벤트를 설정할 때 사용합니다. 이전 글에서 이미 다루었기 때문에 자세한 내용은 생략하겠습니다.

javascript
      export default {
  emits: ['submit'],
  setup(props, { emit }) {
    emit('submit')
  }	
}
    

expose

Options API에서는 상위 컴포넌트에서 하위 컴포넌트의 어떤 변수, 함수든 접근할 수 있습니다. 하지만 Composition API에서는 기본적으로 모두 막고 있어서 접근하려면 따로 expose를 해주어야합니다.

만약 상위 컴포넌트에서 다음과 같이 변수 a, b와 함수 c를 접근한다고 해보겠습니다.

html
      <script setup>
import MyComponent from "./components/MyComponent.vue";
import { ref, onMounted } from "vue";

const comp = ref(null);
onMounted(() => {
  console.log(comp.value.a);
  console.log(comp.value.b);
  comp.value.c();
});
</script>

<template>
  <MyComponent ref="comp"></MyComponent>
</template>
    

하위 컴포넌트에서는 이 값들을 expose 해주면 됩니다.

html
      <script>
import { ref } from "vue";
export default {
  setup(props, { expose }) {
    const a = 1;
    const b = ref(2);
    const c = function () {
      console.log("c");
    };
    expose({ a, b, c });
  },
};
</script>
    

마무리하며

props, attrs, slots, emit, expose 모두 해당 컴포넌트를 사용할 때 주고받는 값들입니다. 이 값들을 적절히 사용할 줄 안다면, 컴포넌트를 더욱 능숙하게 재활용할 수 있게 될겁니다.

html
      <script>
export default {
  setup(props, { attrs, slots, emit, expose }) {
	}
}
</script>
    

script setup

html
      <script setup>
import { ref, useAttrs, useSlots } from 'vue';

const attrs = useAttrs();
const slots = useSlots();
const props = defineProps(['foo']);
defineEmits(['submit']);

const a = 1;
const b = ref(2);
defineExpose({ a, b });
</script>