본문 바로가기
Study/Vue

Vue ) <script setup> 은 또 뭐

by JongIk 2022. 1. 10.
반응형

Vue3 공식문서를 참고했습니다. 제 영어 실력 부족으로 인해 번역이 매끄럽지 않은 부분이 있을 수 있습니다..

<script setup> ??

<script setup> 은 SFC ( Single File Component ) 내에서 컴포지션 API 를 사용하기 위한 컴파일 타임 Syntax sugar ( 더 쉽게 읽고 표현할 수 있도록 설계된 코드 ) 입니다.

SFC 와 컴포지션 API 를 모두 사용하는 경우 권장되며, 일반 <script> 구문과 많은 차이점이 있습니다.

  • 더 적은 상용구로 더 간결한 코드
  • 순수 TypeScript를 사용하여 props 및 emit 이벤트를 선언하는 기능
  • 향상된 Runtime 성능 ( 템플릿은 중간 프록시 없이 동일한 범위의 렌더 함수로 컴파일 )
  • 향상된 IDE 타입 추론 성능 ( 언어 서버가 코드에서 유형을 추출하는 작업 감소)

기본 구문

<script setup>
    console.log('hello script setup')
</script>

내부 코드는 구성 요소 setup() 기능의 내용으로 컴파일됩니다. 다시말해, <script> 구성 요소를 처음 가져올 때는 한 번만 실행되는 normal 과 달리 구성 요소의 인스턴스가 생성될 때마다 <script setup> 의내부 코드가 실행됩니다.

최상위 바인딩은 템플릿에 노출됩니다.

<script setup> 을 사용할 때 내부에 선언된 모든 최상위 바인딩 (변수, 함수 선언 및 import 포함 ) 은 템플릿에서 직접 사용할 수 있습니다.

<script setup>
// variable
const msg = 'Hi!'

// functions
function log() {
  console.log(msg)
}
</script>

<template>
  <div @click="log">{{ msg }}</div>
</template>

import 도 같은 방식으로 노출됩니다. methods 옵션을 통해 노출하지 않고도 템플릿 표현식에서 가져온 Helper 함수를 직접 사용할 수 있습니다.

<script setup>
import { capitalize } from './helpers'
</script>

<template>
  <div>{{ capitalize('hi') }}</div>
</template>

반응성 (Reactivity)

반응성 API 를 사용하여 반응성 상태를 명시적으로 생성해야 합니다. setup() 함수에서 반환된 값과 유사하게 ref 는 템플릿에서 참조될 때 자동으로 unwrapping 됩니다.

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

const count = ref(0)
</script>

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

컴포넌트 사용

<script setup> 범위안의 값은 사용자 정의 구성 요소 태그 이름으로 직접 사용할 수 있습니다.

<script setup>
import MainComponent from './MainComponent.vue'
</script>

<template>
  <MainComponent />
</template>

케밥케이스도 정상적으로 작동하지만 일관성을 위해 파스칼케이스를 사용하는 것이 좋습니다.

동적 컴포넌트

컴포넌트는 문자열 키 아래에 등록되는 대신 변수로 참조되므로 <script setup> 내부에서 동적 컴포넌트를 사용할 때 동적 바인딩 :is 을 사용해야 합니다.

<script setup>
import Foo from './Foo.vue'
import Bar from './Bar.vue'
</script>

<template>
  <component :is="Foo" />
  <component :is="someCondition ? Foo : Bar" />
</template>

재귀 구성 요소

SFC는 파일 이름을 통해 암시적으로 자신을 참조할 수 있습니다. 예를 들어 FooBar.vue 파일은 템플릿에서 <FooBar/>와 같이 자신을 참조할 수 있습니다.

아래 코드의 FooBar 는 import된 컴포넌트보다 우선 순위가 낮습니다. 컴포넌트의 유추된 이름과 충돌하는 import가 있는 경우 import 에 alias 를 지정할 수 있습니다.

import { FooBar as FooBarChild } from './components'

Namespaced 컴포넌트

개체 속성 아래에 중첩된 컴포넌트를 참조하기 위해 <Foo.Bar> 처럼 점과 함께 컴포넌트 태그를 사용할 수 있습니다.

단일 파일에서 여러 컴포넌트를 import 할 때 유용합니다.

<script setup>
import * as Form from './form-components'
</script>

<template>
  <Form.Input>
    <Form.Label>label</Form.Label>
  </Form.Input>
</template>

Using Custom Directives ( 커스텀 디렉티브 사용)

전역으로 등록된 사용자 정의 디렉티브는 예상대로 작동하며, 컴포넌트에 대해 위에서 설명한 것처럼 로컬 디렉티브를 템플릿에서 직접 사용할 수 있습니다.

하지만 한 가지 제한 사항이 있습니다. vNameOfDirective 템플릿에서 직접 사용할 수 있도록 하려면 다음 스키마에 따라 로컬 사용자 정의 디렉티브의 이름을 지정해야합니다.

<script setup>
const vMyDirective = {
  beforeMount: (el) => {
    // do something with the element
  }
}
</script>
<template>
  <h1 v-my-directive>This is a Heading</h1>
</template>
<script setup>
  // imports also work, and can be renamed to fit the required naming schema
  import { myDirective as vMyDirective } from './MyDirective.js'
</script>

defineProps 와 defineEmits

<script setup> 내부에 propsemits 를 선언하기 위해서는 definePropsdefineEmits API 를 사용해야 합니다. 이것은 전체 타입추론을 지원하고 <script setup> 내부에서 자동으로 사용할 수 있게 해줍니다.

<script setup>
const props = defineProps({
  foo: String
})

const emit = defineEmits(['change', 'delete'])
// setup code
</script>
  • definePropsdefineEmits<script setup> 내에서만 사용할 수 있는 컴파일러 매크로입니다. 이것들은 import 할 필요가 없으며 <script setup> 이 처리될 때 컴파일됩니다.
  • defineProps 는 props 옵션과 동일한 값을 허용하고, defineEmits 는 emits 옵션과 동일한 값을 허용합니다.
  • definePropsdefineEmits 는 전달된 옵션을 기반으로 적절한 타입 추론을 제공합니다.
  • definePropsdefineEmits 에 전달된 옵션이 설정에서 모듈 범위로 호이스트됩니다. 따라서 옵션은 설정 범위에 선언된 지역 변수를 참조할 수 없으며 컴파일 오류가 발생합니다.
  • 하지만 import된 바인딩은 모듈 범위에 있으므로 참조할 수 있습니다.

TypeScript 를 사용하는 경우에는 pure type 주석을 이용해 propsemits 를 선언할 수 있습니다.

defineExpose

<script setup> 을 사용하는 컴포넌트는 default 에 의해 종료됩니다. 즉, 템플릿 ref 또는 $parent chain 을 통해 검색되는 컴포넌트의 공개 인스턴스는 <script setup> 내부에 선언된 바인딩을 노출하지 않습니다.

<script setup> 컴포넌트의 속성을 명시적으로 표시하려면 defineExpose 컴파일러 매크로를 사용하세요

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

const a = 1
const b = ref(2)

defineExpose({
  a,
  b
})
</script>

부모가 템플릿 ref를 통해 이 컴포넌트의 인스턴스를 가져오면 검색된 인스턴스는

{ a: number, b: number }

모양입니다. (ref는 일반 인스턴스와 마찬가지로 자동으로 래핑 해제됩니다).

useSlot 와 useAttrs

템플릿엥서 $slots 와 $attrs로 직접 접근할 수 있으므로 <script setup> 내부의 slot 과 attr 사용은 드물어야합니다.

필요한 경우에는 useSlot 과 useAttrs Helpers 를 각각 사용합니다.

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

const slots = useSlots()
const attrs = useAttrs()
</script>

useSlots 와 useAttrs 는 setupContext.slot, setupContext.attrs 와 동등한 값을 반환하는 실제 런타임 함수입니다. 일반 컴포지션 API 기능에서도 사용가능합니다.

일반 <script> 와 함께 사용

<script setup> 은 일반적인 <script> 와 함께 사용할 수 있습니다. 아래와 같은 경우 일반적인 <script> 가 필요할 수도 있습니다.

  • 예를 들어 inheritAttrs 또는 플러그인을 통해 활성화된 사용자 지정 옵션과 같이 <script setup> 에서 표현할 수 없는 옵션을 선언하는 경우.

  • named exports 를 선언합니다.

  • side effect 를 실행하거나 한 번만 실행해야 하는 개체를 만듭니다.

<script>
// normal <script>, executed in module scope (only once)
runSideEffectOnce()

// declare additional options
export default {
  inheritAttrs: false,
  customOptions: {}
}
</script>

<script setup>
// executed in setup() scope (for each instance)
</script>

Top-Level await

Top-level await 는 <script setup> 내에서 사용할 수 있습니다. 결과 코드는 비동기 setup() 으로 컴파일 됩니다.

const post = await fetch(`/api/post/1`).then(r=>r.json())

또한 await 표현식은 await 후 현재 컴포넌트의 인스턴트 컨텍스트를 보존하는 형식으로 자동으로 컴파일 됩니다.

반응형

댓글