현재 프로젝트에서 vue.js 2를 사용중이지만 뷰에 대해 더 배우고 싶어져서 한 권으로 배우는 Vue.js 3 을 구매했다. 앞으로 이 책을 보며 내용을 정리해보려고 한다 (난 잘 까먹으니까,,)
등장하게된 배경
vue는 Options API를 기반으로 하나의 객체를 하나의 모듈로 만들어서 컴포넌트라고 칭하는 라이브러리였다. 하지만 많은 기능들의 산포, 코드 유지비용이 높아지는 문제점을 해결하고자 등장한 컴포지션 API는 함수기반의 방법을 선택했다. 기존 방식을 버린 것은 아니고 프로젝트 규모에 따라 적절하게 방법을 선택할 수 있는 것이다.
컴포지션 API
특정 역할에 따라 함수의 분리를 통해서 가독성이 높고 조직화된 코딩을 할 수 있게 해준다.
컴포지션 API를 통해 은닉화된 코드는 컴포넌트들이 재활용할 수 있다.
Vue 2에서 Mixins를 이용해서 비슷하게 함수를 분리하고 재사용 가능했지만 컴포넌트들이 많아져서 상태관리가 어려웠다. 하지만 컴포지션은 간결한 상태 관리법을 제공해서 Mixins를 대체할 수 있고 vue3로 마이그레이션이 어렵더라도 vue/composition-api 플러그인을 이용하면 사용 가능하다.
Suspense
컴포넌트가 데이터를 받아오기 전까지 기본 컨텐츠를 표시할 수 있는 기능으로 2에서는 v-if, v-show를 사용해서 구현해야했다. 하지만 Suspense는 비동기적 컴포넌트의 로딩이 완료될 때까지 대체 컴포넌트를 그리는 방법을 제시한다. 하지만 아직 사용하는걸 권장하지 않는다고 한다.
Teleport
컴포넌트가 다른 컴포넌트 속에 중첩되고 DOM이 계층구조를 가지면서 css사용이 원하지 않은 결과를 나타낼 때 이를 해결하고자 나타난 것. 리액트에선 Portals로 DOM 계층을 무시하고 특정 엘리먼트에 렌더링할 수 있는 기능을 제공하는데 Vue3에선 Teleport로 어느 컴포넌트든 렌더링 하고 싶은 위치를 사용할 수 있다. (이전에는 portal-vue 서드파티 플러그인을 사용했다)
여러 개의 v-model 디렉티브
양방향 결합 모델인 v-model은 하나의 사용자 컴포넌트가 하나의 디렉티브를 가지는 것을 허용했었다. 추가적으로 필요할 경우 v-bind, v-on 을 이용했으며 여러 변수의 양방향 결합을 위해 변수를 객체나 배열로 만들어 사용하기도 했다. 하지만 vue 3에서는 여러개의 v-model 디렉티브를 하나의 DOM 엘리먼트에 할당할 수 있다. 기본 HTML은 문제되지 않을 수 있지만 사용자 컴포넌트들은 두 개 이상의 양방향 결합 매개변수가 필요한 경우도 있을 수 있다.
ex) 예전 코드
ex) vue3 코드
프록시로 진화된 반응성
기존 Watch옵션은 객체나 배열이 추가되거나 변경되는 것에 반응하지 않았는데 컴포지션 API를 통해서 데이터를 프록시로 변환하여 사용할 수 있게 해준다. 데이터와 프레임워크 사이에서 데이터의 전달 및 변환 관리를 담당하는 프록시는 vue3에서 객체를 프록시 안에 담아두기 때문에 아이템이 추가된 것을 즉시 알아차린다. 대상 객체는 프록시 객체 내부로 들어가고 getter/setter로 관리가 되며 모든 데이터 변화에 반응성을 갖게 된다.
const obj = reactive({})
obj.item1 = 2
이렇게 reactive 함수를 사용하거나 ref를 이용하면 된다
Fragments
하나의 컴포넌트가 여러 개의 루트 노드를 가지는 것으로 vue 2에서도 가능했지만 컴포넌트에 전달되는 Non-props 속성을 컴포넌트에 정의된 루트 노드에 전달하도록 설계되어 있어서 여러개의 루트 노드를 가지면 어느 노드에 속상을 전달해야 할지 애매하기 때문에 경고를 발생시켰다. 이를 해결하고자 vue 템플릿을 생성할 때 하나의 div 태그를 두고 그 안에 Html을 작성하는 것으로 받아들였다. vue 3에서는 걱정없이 여러 개의 루트 노드를 가질 수 있다. Non-Props 속성의 전달이 필요할 경우 어느 노드가 전달을 받을 것인지 명확히 해야된다.
... ... ...Emits Option
emit('click', $event)">OK
export default { emits: [] }
원래 네이티브 이벤트를 발생시키려면 @click 에 native를 입력해서 작성했는데 @click.native
vue3에서 더이상 사용하지 않고 emits옵션에 명시되지 않으면 그냥 네이티브 처리한다.
createRenderer
vue3가 제공하는 런타임 돔과 런타임 코어 패키지는 사용자가 렌더링 동작을 정의할 수 있게 createRenderer라는 함수를 제공하는데 Host환경의 Node와 엘리먼트를 제네릭 인자로 받아 렌더링 동작을 변경할 수 있게 해준다. 즉 crud와 관련된 함수들을 재정의할 수 있다는 것. 이 함수는 하나의 객체를 인자로 받고 해당 객체 내에서 개발자가 재정의하는 함수가 선언되면 된다. insert 함수는 엘리먼트가 삽입될 때 호출되는 함수인데 재정의 하려면 이렇게 사용하면 된다.
const { createApp } = createRenderer({
...
insert: (child, paret, anchor) => {
//엘리먼트 삽입 시 재정의 하고 싶은 코드
}
})