티스토리 뷰

FrontEnd/Vue

[Vue 3] Vue Composition API

devOhzl 2023. 12. 6. 14:29

⚡ Vue 3 는 Composition API가 아니다 !

Vue2에서도 동일하게 Composition API를 사용할 수 있다.

Vue 2 플러그인 👉 Vue 3 API 레벨로 진화

https://vuejs.org/guide/extras/composition-api-faq.html

 

 

Composition API FAQ | Vue.js

Haven't migrated to Vue 3 yet? Explore Never-Ending Support for Vue 2 by HeroDevs Learn more

vuejs.org

Vue vs React 

1️⃣ Vue - Reactivity (반응성)

반응성을 기반으로 데이터가 변했을 때 화면을 변화시킨다.

State 변화를 통해 화면을 갱신시킨다.

 

2️⃣ React - Immutability (불변성)

State가 변했을 때 컴포넌트 코드를 재실행 시키면서 전반적인 화면을 리렌더링 시킨다.

Composition API

컴포넌트 코드 재사용성을 높여주는 API

 

컴포지션은 setup 이라는 API 안에 반응성(Vue Reactivity)을 주입할 데이터를 선언하고 해당 데이터를 제어하는 형태로 작성한다.

 

ref로 정의한 변수는 .value 를 통해 값을 제어할 수 있다.

⭐ setup() 에서 정의한 데이터 등은 return을 해야만 setup() 바깥에서 사용할 수 있다. 

 

setup()

컴포지션 스타일로 코드를 작성하기 위해 꼭 선언해 주어야 하는 옵션

 

📌 ref

- 뷰의 반응성을 주입하는 API

- 기존의 data 속성에 선언했던 값들은 뷰 인스턴스 라이프사이클 과정에서 반응성이 주입된다.

 이 반응성 주입을 좀 더 명시적인 API로 바꾼 것

 

⭐ setup 안에서 ref 로 선언된 값은 항상 return

템플릿 표현식 또는 같은 인스턴스(컴포넌트) 내의 다른 로직에서 접근 할 수 있다. 

import { ref } from 'vue';

createApp({
  setup() {
    const message = ref('');

    return { message }
  },
  methods: {
    changeMessage() {
      this.message = 'Hello Compositon';
    }
  }
});

 

ref로 정의한 변수는 .value 를 통해 값을 제어할 수 있다.

value의 값이 바뀜에 따라 ref가 액세스되거나 변경되었을 때를 감지한다.

// 실제 구현이 아닌 유사 코드
const myRef = {
  _value: 0,
  get value() {
    track()
    return this._value
  },
  set value(newValue) {
    this._value = newValue
    trigger()
  }
}

 

👉 reactive : 객체에 반응성 주입9

 

📌 computed API

- computed API를 라이브러리에서 임포트해야한다.

- computed API를 호출하고 안에 콜백으로 컴퓨티드 속성으로 정의될 값을 작성한다.

<template>
  <div>{{ reversedMessage }}</div>
</template>

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

export default {
  setup() {
    // data
    const message = ref('');

    // computed
    const reversedMessage = computed(() => {
      return message.value.split('').reverse().join('');
    });

    return { reversedMessage }
  }
} 
</script>

 

📌 setup() 에서의 props에서 Destructuring 문법 사용하지 않기

프롭스 속성에 디스트럭처링 문법을 사용하면 반응성(Reactivity)이 사라진다.

 

프롭스 속성이 여러개 정의되어 있을 때 아래와 같이 속성을 꺼내어 사용하면 안된다 !

<script>
export default {
  props: ['items', 'userId', 'title'],
  setup(props) {
    const { items, userId, title } = props;
    console.log(items);
  }
}
</script> 

만약 아래와 같이 사용하게 되면, message 값이 변했을 때 reversedMessage 값이 정상적으로 갱신되지 않는다.

<script>
import { computed } from 'vue';

export default {
  props: ['message'],
  setup(props) {
    const { message } = props;
    
    const reversedMessage = computed(() => {
      return message.split('').reverse().join('');
    });

    return { reversedMessage }
  }
}
</script>

왜냐하면 프롭스 속성인 message 값이 변할 때마다 computed() 로직도 같이 실행이 되어야 하는데

디스트럭처링 문법 사용으로 반응성이 사라졌기 때문에 computed()에서 로직이 실행되지 않기 때문

 

따라서 아래와 같이 props.message 형태로 사용하자.

<script>
import { computed } from 'vue';

export default {
  props: ['message'],
  setup(props) {
    const reversedMessage = computed(() => {
      return props.message.split('').reverse().join('');
    });

    return { reversedMessage }
  }
}
</script>

toRefs() API를 사용하면 디스턱처링 문법을 사용할 수 있다.

<script>
import { computed } from 'vue';

export default {
  props: ['message'],
  setup(props) {
    const { message } = toRefs(props);
    
    const reversedMessage = computed(() => {
      return message.value.split('').reverse().join(''); // 에러 발생: `message.split is not a function`
    });

    return { reversedMessage }
  }
}
</script>

 

📌 watch API

컴포지션(Composition API)에서 사용된 watch 속성

watch 속성 : data, computed, state 등 반응성이 주입된 값이 변했을 때 특정 로직을 실행하게 해주는 속성
데이터가 변했을 때 추가적인 동작을 하기위해 정의할 수 있는 문법
<script>
import { ref, watch } from 'vue';

export default {
  setup() {
    // data
    const message = ref('');

    // watch
    watch(message, (newValue, oldValue) => {
      console.log(newValue);
    });
  }
} 
</script>

 

👉 어떤 값이 변경될 때마다 watch를 통해 api를 호출하거나, localhost에 저장하거나 등등 추가적인 로직을 구현할 수 있다 !

👉 watch가 많아질수록 추적이 어려워져 데이터 관리가 어려워질 수 있으므로 사용은 지양한다.

    → 메서드나 이벤트(emit,prop) 등 선언형으로 구현하기를 추천

 

📌 Lifecycle API

인스턴스 라이프사이클
뷰의 인스턴스가 생성되어 소멸되기까지 거치는 과정을 의미한다.
라이프사이클 훅으로 인스턴스의 특정 시점에 원하는 로직을 구현할 수 있다.
인스턴스 라이프사이클 훅 : 컴포넌트 생명 주기에 따라 특정 로직을 실행할 수 있는 속성 함수. created(), beforeMount() 등
라이프사이클 API : 컴포지션 스타일로 작성된 인스턴스 라이프사이클 훅

 

기존 라이프사이클 훅은 컴포넌트 안에서 아래와 같이 인스턴스 옵션 속성 형태로 작성되었지만

<script>
export default {
  // 인스턴스 옵션 속성 형태로 작성된 라이프사이클 훅
  created() {},
  beforeMount() {},
  mounted() {},
  destroyed() {}
}
</script>

 

라이프사이클 API는 별도의 API를 임포트해서 사용해야 한다.

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

export default {
  setup() {
    // data
    const message = ref('Hello');

    // lifecycle API
    onMounted(() => {
      console.log(message.value);
    });
  }
} 
</script>

 

인스턴스 라이프사이클 훅과 라이프사이클 API의 차이점

setup() 함수가 실행되는 시점이 기존의 beforeCreate()created()가 실행되는 시점과 비슷하기 때문에

아래와 같이 선언할 내용들은 모두 setup() 함수 내에 선언하면 된다.

<script>
export default {
  setup() {
    console.log('API 호출 전');
    axios.get('users/1').then(res => console.log(res));
  }
}
</script>

 

  • beforeCreate, created 대신에 setup() 을 사용
  • onXX 형태로 이름이 바뀜
  • beforeDestory, destroyed 등 destory 대신에 onBeforeUnmount, onUnmounted를 사용
인스턴스 라이프사이클 훅 라이프사이클 API 비고
beforeCreate X 대신 setup() 사용
created X 대신 setup() 사용
beforeMount onBeforeMount  
mounted onMounted  
beforeUpdate onBeforeUpdate  
updated onUpdated  
beforeDestroy onBeforeUnmount destory 라는 표현이 unmount로 변경됨
destroyed onUnmounted destory 라는 표현이 unmount로 변경됨
errorCaptured onErrorCaptured  

 

📌 커스텀 컴포지션 함수

관련된 로직을 하나의 파일에 정리해서 분리할 수 있다.

컴포넌트 동작을 추상화하는 관점에서의 장점이 있다.

  /* App.vue */
  
  import { useTodo } from "./hooks/useTodo";
  
  setup() {
    const { todoItems, addTodoItem, fetchTodos } = useTodo();

    onBeforeMount(() => {
      todoItems.value = fetchTodos();
    });

    return { todoItems, addTodoItem };
  },

👉 동작들을 별도의 파일로 분리하고, 명시적인 동작들만 App.vue에 선언함

/* useTodo.js */

import { ref } from "vue";

function useTodo() {
    const todoItems = ref([]);
    
    function fetchTodos() {
        const result = [];
        for (let i = 0; i < localStorage.length; i++) {
          const todoItem = localStorage.key(i);
          result.push(todoItem);
        }
        return result;
    }

    function addTodoItem(todo) {
        todoItems.value.push(todo);
        localStorage.setItem(todo, todo);
    }
    
    return { todoItems, fetchTodos, addTodoItem }

}

export { useTodo }

 

Composition API 장점

1️⃣ 타입 추론이 용이해졌다.

- Vue 3 라이브러리 내부에서 타입스크립트로 재작성 되었기 때문

 

 

2️⃣ 코드의 재사용성 극대화

로직을 재사용할 수 있게 별도의 파일로 분리해서 import 해올 수 있다.

Composition API 단점

1️⃣ 불필요한 전환 비용

- 로직을 보기위해 분리되어 있는 소스들을 쫓아가야한다. 

- 옵션 API 형태를 이용해서 컴포넌트를 작게 만들어 조합하는 형태로 설계를 해도 충분하다.

  간결한 코드에서는 setup() 을 사용하지 않아도 된다. ( 동일한 코드가 반복되는 경우에 모듈화를 위해 사용 )

 

2️⃣ 코드의 복잡성

setup() 안에 data, methods, computed 를 몰아버리게 되면 쉽게 각 API를 구분할 수 없는 상황이 생길 수도 있다.

가급적이면 코드를 재사용할 때 setup()을 사용하는 것 추천

 

👉 인스턴스 옵션 방식을 최대한 활용하지만,

컴포넌트가 복잡해졌을 때 Composition API, 즉 setup()을 사용해서 로직을 분리하고 재활용해보기

 

 

 

참고

[인프런] Vue 3 시작하기

Cracking Vue.js

https://ko.vuejs.org/guide/essentials/reactivity-fundamentals

 
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
글 보관함