Vue 3의 흥미로운 새 기능

업데이트: Link

Vue 3의 흥미로운 새 기능

Written by Filip Rakowski

이전 기사에서 우리는 Vue 3가 가져올 성능 향상에 대해 배웠습니다. 우리는 이미 Vue의 새로운 주요 버전으로 작성된 앱이 매우 잘 수행되지만 성능이 가장 중요한 부분은 아니라는 것을 알고 있습니다. 우리 개발자들에게 가장 중요한 것은 새 릴리스가 코드 작성 방식에 어떤 영향을 미칠 것인지입니다.

예상대로 Vue 3는 새롭고 흥미로운 기능을 많이 제공합니다. 고맙게도 Vue 팀은 주요 변경 사항보다는 현재 API에 대한 추가 및 개선 사항을 대부분 도입했기 때문에 Vue 2를 이미 알고 있는 사람들은 새로운 구문에 빠르게 익숙해질 것입니다.

대부분의 사람들이 들어봤을 API부터 시작해 보겠습니다.

구성 API - Composition API

구성 API는 Vue의 다음 주요 버전에서 가장 일반적으로 논의되고 특징적인 구문입니다. 로직 재사용 및 코드 구성에 대한 완전히 새로운 접근 방식입니다.

현재 우리는 옵션 API라고 하는 구성 요소를 빌드합니다. Vue 컴포넌트에 로직을 추가하기 위해 data, methods, computed 등과 같은 속성을 채웁니다. 이 접근 방식의 가장 큰 단점은 이것이 작동하는 JavaScript 코드 자체가 아니라는 사실입니다. 템플릿에서 액세스할 수 있는 속성과 this 키워드의 동작을 정확히 알아야 했습니다. 내부적으로 Vue 컴파일러는 이 속성을 작업 코드로 변환해야 합니다. 그 때문에 우리는 자동 제안이나 유형 검사와 같은 이점을 누릴 수 없습니다.

구성 API는 구성 요소 속성을 통해 현재 사용할 수 있는 메커니즘을 JavaScript 기능으로 노출하여 이 문제를 해결하는 것을 목표로 합니다. Vue 핵심 팀은 Composition API를 “구성 요소 로직의 유연한 구성을 허용하는 추가 기능 기반 API 세트”라고 설명합니다. 컴포지션 API로 작성된 코드는 더 읽기 쉽고 뒤에서 마법을 부리지 않아 읽거나 배우기가 더 쉽습니다.

작동 방식을 이해하기 위해 새로운 Composition API를 사용하는 구성 요소의 매우 간단한 예를 살펴보겠습니다.

<template>
  <button @click="increment">
    Count is: 8, double is , click to increment.
  </button>
</template>

<script>
import { ref, computed, onMounted } from 'vue'

export default {
  setup() {
    const count = ref(0)
    const double = computed(() => count.value * 2)

    function increment() {
      count.value++
    }

    onMounted(() => console.log('component mounted!'))

    return {
      count,
      double,
      increment
    }
  }
}
</script>

이제 이 코드를 조각으로 나누어 무슨 일이 일어났는지 이해해 보겠습니다.

import { ref, computed, onMounted } from 'vue'

앞에서 언급했듯이 구성 API는 구성 요소 속성을 함수로 노출하므로 첫 번째 단계는 필요한 함수를 가져오는 것입니다. 우리의 경우 ref로 반응 참조를 생성하고 computed로 계산된 속성을 생성하고 onMounted로 마운트된 수명 주기 후크에 액세스해야 합니다.

이제 이 신비한 setup 함수가 무엇인지 궁금할 것입니다.

export default {
    setup() {
    }
}

간단히 말해서 템플릿에 속성과 기능을 반환하는 함수일 뿐입니다. 그게 다야 여기에서 모든 반응 속성, 계산 속성, 감시자 및 수명 주기 후크를 선언한 다음 템플릿에서 사용할 수 있도록 반환합니다.

setup 함수에서 반환하지 않는 것은 템플릿에서 사용할 수 없습니다.

const count = ref(0)

위의 내용에 따르면 ref 기능을 사용하여 count라는 반응 속성을 선언하고 있습니다. 프리미티브 또는 객체를 래핑하고 반응 참조를 반환할 수 있습니다. 전달된 요소의 값은 생성된 참조의 value 속성에 유지됩니다. 예를 들어 count 참조 값에 액세스하려면 count.value를 명시적으로 요청해야 합니다.

const double = computed(() => count.value * 2)

function increment() {
  count.value++
}

… 그리고 이것은 우리가 계산된 속성 doubleincrement 함수를 선언할 때 했던 것과 정확히 같습니다.

onMounted(() => console.log('component mounted!'))

onMounted 후크를 사용하면 구성 요소가 마운트될 때 몇 가지 메시지를 기록하여 할 수 있음을 보여줍니다 😉

return {
  count,
  double,
  increment
}

마지막에는 템플릿에서 사용할 수 있도록 increment 메서드를 사용하여 countdouble 속성을 반환합니다.

<template>
  <button @click="increment">
    Count is: 8, double is . Click to increment.
  </button>
</template>

그리고 짜잔! 이제 이전 Options API를 통해 선언된 것과 동일한 방식으로 템플릿의 setup 메서드에서 반환된 속성 및 함수에 액세스할 수 있습니다.

이것은 옵션 API로도 쉽게 달성할 수 있는 간단한 예입니다. 새로운 Composition API의 진정한 이점은 단순히 다른 방식으로 코딩하는 것이 아니라 코드/로직을 재사용할 때 그 이점이 드러납니다.

컴포지션 API를 사용한 코드 재사용

새로운 컴포지션 API에는 더 많은 이점이 있습니다. 코드 재사용에 대해 생각해 보십시오. 현재 다른 구성 요소 간에 일부 코드를 공유하려는 경우 믹스인 및 범위 지정 슬롯의 두 가지 옵션을 사용할 수 있습니다. 둘 다 단점이 있습니다.

counter 기능을 추출하여 다른 구성 요소에서 재사용하려고 한다고 가정해 보겠습니다. 아래에서 사용 가능한 API 및 새 구성 API와 함께 사용하는 방법을 확인할 수 있습니다.

믹스인부터 시작하겠습니다.

import CounterMixin from './mixins/counter'

export default {
  mixins: [CounterMixin]
}

믹스인의 가장 큰 단점은 믹스인이 실제로 컴포넌트에 추가하는 것에 대해 아무것도 모른다는 사실입니다. 추론하기 어려울 뿐만 아니라 기존 속성 및 함수와의 이름 충돌로 이어질 수 있습니다.

범위 지정 슬롯이 필요한 때입니다.

<template>
  <Counter v-slot="{ count, increment }">
     8
    <button @click="increment">Increment</button> 
  </Counter> 
</template>

범위가 지정된 슬롯을 사용하면 v-slot 속성을 통해 액세스할 수 있는 속성을 정확히 알고 있으므로 코드를 훨씬 더 쉽게 이해할 수 있습니다. 이 접근 방식의 단점은 템플릿에서만 액세스할 수 있고 ‘카운터’ 구성 요소 범위에서만 사용할 수 있다는 것입니다.

이제 컴포지션 API를 사용할 차례입니다.

function useCounter() {
  const count = ref(0)
  function increment () { count.value++ }

  return {
    count,
    incrememt
  }
}

export default {
  setup () {
    const { count, increment } = useCounter()
    return {
      count,
      increment
    }
  }
}

훨씬 더 우아하지 않나요? 템플릿과 구성 요소 범위 모두에 의해 제한되지 않으며 카운터에서 액세스할 수 있는 속성을 정확히 알고 있습니다. 또한 useCounter는 일부 속성을 반환하는 기능일 뿐이기 때문에 편집기에서 사용할 수 있는 코드 완료의 이점을 누릴 수 있습니다. 배후에는 마법이 없기 때문에 에디터가 유형 확인과 제안을 도와줄 수 있습니다.

타사 라이브러리를 사용하는 보다 우아한 방법이기도 합니다. 예를 들어 Vuex를 사용하려면 Vue 프로토타입(this.$store)을 오염시키는 대신 useStore 함수를 명시적으로 사용할 수 있습니다. 이 접근 방식은 Vue 플러그인의 숨겨진 마법도 지웁니다.

const { commit, dispatch } = useStore()

Composition API와 사용 사례에 대해 자세히 알고 싶다면 Vue 팀의 이 문서에서 새로운 API의 이유를 설명하고 제안하는 것을 적극 권장합니다. 최고의 사용 사례입니다. Vue 핵심 팀의 Thorsten Lünborg가 구성 API를 사용한 예가 있는 훌륭한 저장소도 있습니다.

글로벌 마운팅/구성 API 변경

애플리케이션을 인스턴스화하고 구성하는 방식에서 또 다른 주요 변경 사항을 찾을 수 있습니다. 지금 어떻게 작동하는지 봅시다:

import Vue from 'vue'
import App from './App.vue'

Vue.config.ignoredElements = [/^app-/]
Vue.use(/* ... */)
Vue.mixin(/* ... */)
Vue.component(/* ... */)
Vue.directive(/* ... */)

new Vue({
  render: h => h(App)
}).$mount('#app')

현재 우리는 설정을 제공하고 새로운 Vue 인스턴스를 생성하기 위해 전역 Vue 객체를 사용하고 있습니다. Vue 객체에 대한 변경 사항은 모든 Vue 인스턴스와 구성 요소에 영향을 미칩니다.

이제 Vue 3에서 어떻게 작동하는지 봅시다.

import { createApp } from 'vue'
import App from './App.vue'

const app = createApp(App)

app.config.ignoredElements = [/^app-/]
app.use(/* ... */)
app.mixin(/* ... */)
app.component(/* ... */)
app.directive(/* ... */)

app.mount('#app')

이제 눈치채셨겠지만 모든 구성은 createApp으로 정의된 특정 Vue 애플리케이션으로 범위가 지정됩니다.

코드를 더 쉽게 이해하고 타사 애드온으로 인해 발생하는 예기치 않은 문제를 덜 발생시킬 수 있습니다. 현재 일부 타사 솔루션이 Vue 개체를 수정하는 경우 Vue 3에서는 불가능한 예기치 않은 방식(특히 글로벌 믹스인 사용)으로 애플리케이션에 영향을 미칠 수 있습니다.

이 API 변경 사항은 현재 이 RFC에서 논의되며, 이는 향후 변경될 수 있음을 의미합니다.

Fragments

Vue 3에서 기대할 수 있는 또 다른 흥미로운 추가 기능은 Fragments입니다.

질문할 수 있는 조각은 무엇입니까? Vue 구성 요소를 만들면 루트 노드가 하나만 있을 수 있습니다.

이는 다음과 같은 구성 요소를 생성할 수 없음을 의미합니다.

<template>
  <div>Hello</div>
  <div>World</div>
</template>

그 이유는 Vue 구성 요소를 나타내는 Vue 인스턴스가 단일 DOM 요소에 바인딩되어야 하기 때문입니다. 여러 DOM 노드가 있는 구성 요소를 만들 수 있는 유일한 방법은 기본 Vue 인스턴스가 없는 기능 구성 요소를 만드는 것입니다.

React 커뮤니티에도 같은 문제가 있는 것으로 나타났습니다. 그들이 생각해 낸 솔루션은 Fragment라는 가상 요소였습니다. 대략 이렇게 생겼습니다.

class Columns extends React.Component {
  render() {
    return (
      <React.Fragment>
        <td>Hello</td>
        <td>World</td>
      </React.Fragment>
    );
  }
}

Fragment는 일반 DOM 요소처럼 보이지만 가상이며 DOM 트리에서 전혀 렌더링되지 않습니다. 이렇게 하면 중복 DOM 노드를 만들지 않고 구성 요소 기능을 단일 요소에 바인딩할 수 있습니다.

현재 vue-fragments 라이브러리와 함께 Vue 2에서 프래그먼트를 사용할 수 있으며 Vue 3에서 바로 사용할 수 있습니다.

Suspense

Vue 3에서 채택될 React 생태계의 또 다른 훌륭한 아이디어는 Suspense 구성 요소입니다.

Suspense는 구성 요소 렌더링을 일시 중단하고 조건이 충족될 때까지 대체 구성 요소를 렌더링합니다. Vue London Evan 동안 이 주제를 간단히 다루었고 우리가 어느 정도 예상할 수 있는 API를 보여주었습니다. 결과적으로 Suspense는 슬롯이 있는 구성 요소일 뿐입니다.

<Suspense>
  <template >
    <Suspended-component />
  </template>
  <template #fallback>
    Loading...
  </template>
</Suspense>

대체 콘텐츠는 Suspended-component가 완전히 렌더링될 때까지 표시됩니다. Suspense는 비동기 구성 요소인 경우 구성 요소가 다운로드될 때까지 기다리거나 setup 함수에서 일부 비동기 작업을 수행할 수 있습니다.

Multiple v-models

V-model은 주어진 구성 요소에서 양방향 바인딩을 달성하는 데 사용할 수 있는 지시문입니다. 반응 속성을 전달하고 구성 요소 내부에서 수정할 수 있습니다.

우리는 form 요소의 v-model을 잘 압니다.

<input v-model="property" />

그러나 모든 구성 요소에 v-model을 사용할 수 있다는 것을 알고 계셨습니까? 내부적으로 v-modelvalue 속성을 전달하고 input 이벤트를 수신하기 위한 지름길입니다. 위의 예를 아래 구문으로 다시 작성하면 정확히 동일한 효과가 나타납니다.

<input 
  v-bind:value="property"
  v-on:input="property = $event.target.value"
/>

구성 요소 model 속성을 사용하여 기본 속성 및 이벤트의 이름을 변경할 수도 있습니다.

model: {
  prop: 'checked',
  event: 'change'
}

보시다시피 v-model 지시문은 구성 요소에 양방향 바인딩을 사용하려는 경우 매우 유용한 구문 전달장치가 될 수 있습니다. 불행히도 구성 요소당 하나의 v-model만 가질 수 있습니다.

다행히 Vue 3에서는 문제가 되지 않습니다! v-model 속성 이름을 지정하고 원하는 만큼 가질 수 있습니다. 아래에서 양식 구성 요소에 있는 두 개의 v-model의 예를 찾을 수 있습니다.

<InviteeForm
  v-model:name="inviteeName"
  v-model:email="inviteeEmail"
/>

이 API 변경 사항은 현재 이 RFC 에서 논의되며, 이는 향후 변경될 수 있음을 의미합니다.

Portals

포털은 현재 구성 요소 외부의 특정 콘텐츠를 렌더링하기 위한 특수 구성 요소입니다. React에서 기본적으로 구현된 기능 중 하나이기도 합니다. 포털에 대한 React 문서의 내용은 다음과 같습니다.

Portals는 부모 구성 요소의 DOM 계층 외부에 존재하는 DOM 노드로 자식을 렌더링하는 최고의 방법을 제공합니다.

페이지 상단에 표시되는 모달, 팝업 및 일반적으로 구성 요소를 처리하는 매우 좋은 방법입니다. 포털을 사용하면 호스트 구성 요소 CSS 규칙 중 어느 것도 표시하려는 구성 요소에 영향을 미치지 않으며 z-index로 악의적인 해킹을 하지 않도록 할 수 있습니다.

모든 포털에 대해 포털 콘텐츠가 렌더링될 대상 대상을 지정해야 합니다. 아래에서 이 기능을 Vue 2에 추가하는 portal-vue 라이브러리의 구현을 볼 수 있습니다.

<portal to="destination">
  <p>This slot content will be rendered wherever thportal-target with name 'destination'
    is  located.</p>
</portal>

<portal-target name="destination">
  <!--
  This component can be located anywhere in your App.
  The slot content of the above portal component wilbe rendered here.
  -->
</portal-target>

Vue 3는 포털에 대한 기본 지원과 함께 제공됩니다!

새로운 커스텀 디렉티브 API

사용자 지정 지시문 API는 구성 요소 수명 주기에 더 잘 맞도록 Vue 3에서 약간 변경됩니다. 이 변경으로 인해 API가 더 직관적이기 때문에 신규 사용자가 API를 더 쉽게 이해하고 배울 수 있습니다.

이것은 현재 사용자 정의 지시문 API입니다.

const MyDirective = {
  bind(el, binding, vnode, prevVnode) {},
  inserted() {},
  update() {},
  componentUpdated() {},
  unbind() {}
}

… Vue 3에서는 이렇게 보일 것입니다.

const MyDirective = {
  beforeMount(el, binding, vnode, prevVnode) {},
  mounted() {},
  beforeUpdate() {},
  updated() {},
  beforeUnmount() {}, // new
  unmounted() {}
}

브레이킹 체인지이긴 하지만 Vue 호환성 빌드로 쉽게 다루어야 합니다.

이 API 변경 사항은 현재 이 RFC에서 논의되어 향후 변경될 수 있습니다.

쉿! 우리 과정에서 사용자 지정 지시문을 마스터하는 방법을 배울 수 있습니다.

요약

Vue 3의 가장 큰 새로운 API인 Composition API 외에도 작은 개선 사항도 많이 있습니다. 우리는 Vue가 더 나은 개발자 경험과 더 간단하고 직관적인 API를 향해 나아가고 있음을 알 수 있습니다. Vue 팀이 현재 타사 라이브러리를 통해서만 사용할 수 있는 많은 아이디어를 프레임워크의 핵심에 채택하기로 결정한 것도 좋습니다.

위 목록에는 주요 API 변경 및 개선 사항만 포함되어 있습니다. 다른 것들이 궁금하시다면 Vue RFCs repository.를 확인해주세요.

태그: ,

카테고리:

업데이트:

댓글남기기