본문 바로가기
front/vue

[vue] setup()

by juniKang 2022. 7. 30.

Note

여기서는 setup 컴포넌트 옵션의 사용법을 다룬다. Single-File Component로 컴포지션 API를 사용한다면, <script setup> 이 보다 간결하므로 추천한다.

 

setup() 훅은 다음과 같은 케이스에 있는 컴포넌트에서 컴포지션 API 사용을 위한 진입점으로서 제공된다:

  1. 빌드 스텝 없이 컴포지션 API를 사용할 때;
  2. 옵션 API 컴포넌트에서 컴포지션 API 기반 코드로 통합 할 때;

Basic Usage

ref(), reactive()와 같은 반응형 APIs 를 사용해서 반응형 상태를 선언하고, setup에서 리턴되는 객체로 반응형 상태들을 템플릿에서 사용할 수 있다. 리턴 객체에있는 프로퍼티들은 컴포넌트 인스턴스로 만들어질 수 있다. (만약 다른 옵션들이 사용되면) :

<script>
import { ref } from 'vue'

export default {
  setup() {
    const count = ref(0)
    
    // expose to template and other options API hooks
    return {
      count
    }
  },
  
  mounted() {
    console.log(this.count) // 0
  }
}
</script>

<template>
  <button @click="count++">{{ count }}</button>
</template>

setup으로부터 리턴되는 refs들은 템플릿에서 사용할 때 자동적으로 얕은 언래핑이 된다. 그래서 사용할 때 .value를 붙이지 않아도 된다. this에 접근할때도 마찬가지로 언래핑된다. 

 

Tip

 setup()은 컴포넌트 인스턴스에 접근할 수 없다. this는 setup() 안에서는 undefined 값을 갖는다. 옵션 API에서 컴포지션 API가 내보낸 값들을 접근할 수 있지만, 반대로 컴포지션 API에서 옵션API에 접근하는건 불가능 하다.

 


Accesing Props

setup 함수에있는 첫번째 아규먼트는 props 아규먼트 이다. 일반적인 컴포넌트에서 기대하는 것처럼, setup 안에 props들은 반응형이고, 새로운 props가 들어올 때마다 업데이트 된다. 

export default {
  props: {
    title:String
  },
  setup(props) {
    console.log(props.title)
  }
}

props 객체를 분할할당(destructure)하면, 할당된 변수들은 반응형을 잃게 된다. 그렇기 떄문에 항상 props.xxx의 폼으로 props에 접근하는 것을 추천한다.

// 이렇게 쓰면 안된다.
  setup({apple, banana}) {...}
  
// props.xxx 폼을 유지하라.
  setup(props) {
    props.apple;
  }

props를 정말로 분할할당할 필요가 있다면, 또는 반응형을 유지하면서 외부 함수에 prop을 넘겨야 한다면, toRefs()나 toRef() 유틸리티 API를 사용할 수 있다:

import { toRefs, toRef } from 'vue'

export default {
  setup(props) {
    // 'props'를 refs의 객체로 변환한 다음 분할할당 하라.
    const { title } = toRefs(props)
    // 'title' 은 'props.title'을 추적하는 ref 이다.
    console.log(title.value)
    
    // 또는, 'props'에 있는 싱글 프로퍼티를 ref로 바꾼다.
    const title = toRef(props, 'title')
  }
}

Setup Context

setup 함수에 넘겨지는 두 번째 아규먼트는 Setup Context 오브젝트이다. 이 context 오브젝트는 setup안에서 유용한 다른 값들을 노출한다 :

export default {
  setup(props, context) {
    // 애트리뷰트 (반응형이 아닌 객체, $attrs와 동일)
    console.log(context.attrs)
    
    // Slots (반응형이 아닌 객체, $slots와 동일)
    console.log(context.slots)
    
    // Emit events (함수, $emit과 동일)
    console.log(context.emit)
    
    // Expose 되는 퍼블릭 프로퍼티 (함수)
    console.log(context.expose)
  }
}

context 객체는 반응형이 아니어서 분할할당에 안전하다:

export default {
  setup(props, { attrs, slots, emit, expose }) {
    ...
  }
}

attrs와 slots는 상태를 담고있는 객체들이어서 컴포넌트가 업데이트될 때마다 항상 업데이트 된다. 그래서 사용할 때 항상 attrs.x 나 slots.x로 참조해야만하고 분할할당 해서는 안된다. 또한 알아둬야 할 것은, props와 다르게 attrs와 slots의 프로퍼티들은 반응형이 아니라는 것이다. 만약 attrs나 slots의 변화에 기반한 사이드이펙트를 적용할 생각이라면, onBeforeUpdate 라이프사이클 훅안에서 적용할 수 있다.

 

Exposing Public Properties

expose 는 템플릿 refs를 통해 부모 컴포넌트가 컴포넌트 인스턴스에 접근할 때, 노출되는 프로퍼티를 명시적으로 제한하는 데에 사용되는 함수다.

export default {
  setup(props, { expose }) {
    // 인스턴스를 "closed" 되도록 만든다 -
    // 즉, 부모에 어떤것도 노출시키지 않는다.
    expose()
    
    const publicCount = ref(0)
    const privateCount = ref(0)
    // 지역 상태를 선택적으로 노출한다
    expose({ count: publicCount })
  }
}

Usage with Render Functions

setup은 직접적으로 같은 스코프에서 선언된 반응형 상태를 사용하는 렌더 함수를 리턴할 수 있다:

import { h, ref } from 'vue'

export default {
  setup() {
    const count = ref(0)
    return () => h('div', count.value)
  }
}

반환되는 렌더 함수는 우리가 다른 어떤 것도 리턴하지 않게 한다. 내부적으로 이건 문제가 되지 않지만, 템플릿 ref를 통해 부모 컴포넌트로 컴포넌트의 메소드를 노출하길 원할때 문제가 될 수 있다.

 

expose()를 사용해 이 문제를 해결할 수 있다 :

import { h, ref } from 'vue'

export default {
  setup(props, { expose }) {
    const count = ref(0)
    const increment = () => ++count.value
    
    expose({
      increment
    })
    
    return () => h('div', count.value)
  }
}

increment 메소드는 template ref를 통해서 부모 컴포넌트에서 사용할 수 있다. 

'front > vue' 카테고리의 다른 글

[vue] ref, reactive 반응형  (0) 2022.08.07
[vue] app.mount()  (0) 2022.07.30
[vue] cloneVNode()  (0) 2022.07.30
[vue]mergeProps()  (0) 2022.07.30
[Vue] h()  (1) 2022.07.30

댓글