본문 바로가기
Framework/vue_Architecture

composition API(1)_반응성(vue3.js)

by cariño 2022. 12. 30.
728x90
반응형

Vue 3.0 새로 등장한 Vue.js Composition API

기존 인스턴스 옵션 단위가 아닌, 특정 기능이나 논리의 단위로 코드를 그룹화 하는것이 목적인  함수 기반의 API이다.

그룹화된 로직을 여러 컴포넌트에서 재사용할 수 있도록 코드의 재사용성과 타입 추론(타입스크립트)이 크게 개선되었다고 한다. 

 

 setup 함수는 beforeCreate 이전에 setup이 호출된다. (조기화 진입점, 마운트되기 전에 항상 실행)

컴포지션 API를 사용하는 경우 beforeCreate, created 훅 대신 setup을 사용한다.

 

인스턴스가 생성되기 전의 시점이기 때문에 컴포넌트 인스턴스에 접근하는 기능을 사용할 수 없다.

그렇기 때문에 setup 컴포넌트 안에서 this는 methos, data, computed등의 선언들에 접근을 할 수 없다.


 

[composition api를 사용하지 않았을 때]

 

반응형데이터 :  값이 변경됨에 따라 이를 감지하고 종속된 작업(side effect)가 실행 된다.

setup 함수를 이용하여 데이터를 전달하는 상황일때 만일 반응성을 이용하지 않는다면?
어떠한 값의 변경이 일어났을 때 해당 데이터를 사용하고 있는 기능, 화면에서 변화를 감지하지 않기에 상태를 유지할 수가 없게 된다.

 

vue2에서는 mutable한 값은 data에서 관리해왔다.  

<template>
    <h1>Home</h1>
    <p>My name is {{ name }} and my age is {{ age }}</p>
</template>

<script>
export default {
    data(){
        return{
            name: 'bini',
            age: 33
        }
    },
</script>

 

하나의 컴포넌트를 만들기 위해서 아래 로직처럼 data외에 값을 조작하는 methods와 종속된 대상의 캐싱값을 반환받으면 변경이 일어나는 computed등이 사용되어진다. 기존 이러한 사용 방식은 직관적이지 않고 재사용성을 떨어트리는 단점을 갖고있다. 

 

<template>
    <h1>Home</h1>
    <p>{{ countUntil }}</p>
    <button @click="increase">increase</button>
    <button @click="decrease">decrease</button>
</template>

<script>
export default {
    data() {
        return {
            count: 0,
        };
    },
    computed: {
        // 저장된 결과(캐싱)를 반환하므로 종속 대상의 변경이 일어나기 전까지 호출 되지 않는다.
        countUntil() {
            return this.count + "입니다";
        },
    },
    methods: {
        increase() {
            ++this.count;
        },
        decrease() {
            --this.count;
        },
    },
};
</script>

<style></style>

위 코드처럼 하나의 기능임에도 불구하고 이곳 저곳에서 사용되어 지고 있는데, 만일 규모가 커지게 된다면 복잡한 로직에서 관리하기에 힘들어질 수 있다. 

 

 


 

 

 

 

컴포지션 API에서는 2가지 유형(reactive, ref)의 변경 가능한 반응형 데이터를 만들 수 있다.

프록시 객체: https://doqtqu.tistory.com/346

 

 

 

[ref]

<template>
  <div>
    <input type="text" ref="target">
  </div>
</template>

<script>
  export default {
    mounted(){
      console.log('target', this.$refs.target);
    }
  }
</script>

기존 vue에서 refs,

setup안에서 사용이 아닌 일반적으로 ref를 사용할때는 this.$refs~ 를 사용한다. 

당연한 얘기겠지만, 렌더링이 완료된 직 후 ref를 사용할 수 있기에 mounted() 함수안에서 사용한다. 

 

 

컴포지션 API의 ref를 통해 객체를 생성할 수 있다. 

refs는 원시 타입을 포함한 여러가지 데이터 타입을 수용할 수 있다. 

- 템플릿안에서 ref는 value 속성에 대한 접근없이 값 자체를 사용한다. 

 

<template>
    <h1>Home</h1>
    <p ref="p">My name is {{ name }} and my age is {{ age }}</p>
    <button @click="handleClick">click me</button>
    <button @click="age++">{{ age }}</button>
</template>

<script>
import { ref } from "@vue/reactivity";
export default {
    setup() {
        console.log(this); //undefined

        const name = ref("bini");
        const age = ref(30);
        const p = ref(null);

        const handleClick = () => {
            name.value = "luigi";
            age.value = 20;
        };
        return { name, age, handleClick, p };
    },
};
</script>

 

원본 값은 ref.value 속성을 통해 접근할 수 있다. 값을 변경할 때도 value속성에 접근하여 조작해야한다.

reactive는 원본 수정 시 value없이 바로 속성 접근가능

 

 

 

 

[reactive]

- composition api를 통해서만 생성할 수 있다.

- 객체만 받는다. 

-  reactive로 생성한 프록시 객체는 원본 객체와 완전히 동일하다.

- 값에 접근할 때는 원본 객체에 접근하는 방식과 같다.

- 모두 deep 한 객체를 받는다. reactive로 생성한 프록시 객체는 원본 객체와 완전히 동일

<template>
    <h2>Refs</h2>
    <p>{{ ninjaOne.name }} - {{ ninjaOne.age }}</p>
    <button @click="updateNinja">update ninja one</button>

    <h2>Reactive</h2>
    <p>{{ ninjaTwo.name }} - {{ ninjaTwo.age }}</p>
    <button @click="updateWww">update ninja two</button>
</template>

<script>
import { ref, reactive } from "@vue/reactivity";
export default {
    setup() {
        const ninjaOne = ref({ name: "ninJa", age: 33 });
        const ninjaTwo = reactive({ name: "www", age: 22 });
        const updateNinja = () => {
            ninjaOne.value.age = 40;
        };
        const updateWww = () => {
            ninjaTwo.age = 40;
        };

        return { ninjaOne, updateNinja, ninjaTwo, updateWww };
    },
};
</script>

값에 접근할 때 ninjaTwo.name으로 받을 수 있다. 

reactive를 통해 생성된 객체는 모두 깊은 감지를 수행하기 때문에 객체가 중첩된 상황에서도 반응성 데이터를 쉽게 조작하고 처리할 수 있다. 

 


toRefs()

만약에 state를 반복해서 사용하는 상황일 경우?
애초에 보낼 때 반응성을 가진 데이터를 속성값을 써서 보내면 된다고 생각할 수 있다.
하지만 return에 속성 값을 직접 입력하게 된다면 반응성을 잃게 된다. 이런 상황에선 toRefs를 이용할 수 있다.

<template>
    <p>{{ username.name }}</p>
</template>

<script>
import { reactive } from "@vue/reactivity";
export default {
    setup() {
        const username = reactive({
            name: "ninja",
            age: 3,
        });
        return { username };
    },
};
</script>
  • username.name을 여러개 사용해야 한다면?

 

<template>
    <p>{{ username }}</p>
</template>

<script>
import { reactive } from "@vue/reactivity";
export default {
    setup() {
        const username = reactive({
            name: "ninja",
            age: 3,
        });
        return { username.name };
    },
};
</script>
  • username.name을 직접 return 시키면 되지 않나?
    정답은 No! 직접 속성값을 전달하게 되면 반응성을 잃게 된다.
    (상태 변화에 대한 감지가 불가능하게 된다는 말)

 

<template>
    <p>{{ name }}</p>
    <p>{{ age }}</p>
</template>

<script>
import { reactive, toRefs } from "@vue/reactivity";
export default {
    props: ["post"],
    setup() {
        const username = reactive({
            name: "ninja",
            age: 3,
        });
        return toRefs(username);
    },
};
</script>

 

 


 

 반응성 주입시켜보자

<template>
        <p>{{ isNotUgly }}</p>
</template>

<script>
import { ref } from "@vue/reactivity";
export default {
    setup() {
        const isHandsome = false;
        const isNotUgly = isHandsome;
        isHandsome = true;
        return { isNotUgly };
    },
};
</script>

일반적인 javascript 형태일 경우의 형태로 입력을 했을 때 isHansome의 값이 바뀌어도 isNotUgly값은 바뀌지 않는다.
vue에서는 isNotUgly값 또한 변화를 기대하기에 반응성을 주입을 시켜줘야한다.

 

<template>
        <p>{{ isNotUgly }}</p>
</template>

<script>
import { ref } from "@vue/reactivity";
export default {
    setup() {
        const isHandsome = ref(false);
        const isNotUgly = isHandsome;
        isHandsome.value = true;
        return { isNotUgly };
    },
};
</script>

단일의 값인 경우는 ref를 이용할 수 있다.
더 다양한 데이터는 reative를 사용해서 보낼 수 있다.

 

<template>
        <p>{{ isHandsome }}</p>
        <p>{{ username.name }}</p>
</template>

<script>
import { reactive, ref, toRefs } from "@vue/reactivity";
export default {
    setup() {
        const isHandsome = ref(false);
        const username = reactive({
            name: "ninja",
            age: 3,
        });
        return { isHandsome, username };
    },
};
</script>

 

toRefs() 

<template>
        <p>{{ name }}</p>
</template>

<script>
import { reactive, toRefs } from "@vue/reactivity";
export default {
    setup() {
        const username = reactive({
            name: "ninja",
            age: 3,
        });
        return { ...toRefs(username) };
    },
};
</script>

object형태로 값을 보내기때문에 스프레드 연산자를 이용해서 전달해야한다.

 

<template>

    <div>{{ username }}</div>
    <input type="text" v-model="username" />
    <button @click="changeName">이름 변경</button>

    <div>제품명: {{ name }}, 가격: {{ price }}</div>
    <button @click="changeProduct">제품 변경</button>
    
</template>

<script>
import { reactive, ref, toRefs } from "@vue/reactivity";
export default {
    setup() {
        const username = ref("scalp");
        function changeName() {
            username.value = "messi";
        }

        const productList = reactive({
            name: "tv",
            price: 1000,
        });
        function changeProduct() {
            (productList.name = "세탁기"), (productList.price = 5000);
        }

        return {
            username,
            changeName,
            ...toRefs(productList),
            changeProduct,
        };
    },
};
</script>
 

 


 

refs와 reactive 차이점?

 

1. 타입 제한

ref는 string, number, object 등 어떠한 타입형태도 사용이 가능하다 .

reactive는 array, object, set, map 과 같은 타입에서만 사용할 수 있다.

(reactive는 Primitive Type(number, string, boolean ...)반응형 상태를 선언시 반응성이 유지되지 않는다.)

 

2. 접근 방식

ref는 value 속성을 접근할 수 있다. 

reactive는 value속성 접근없이 반응성으로 사용한다. 

 

3. 장점

ref는 타입의 제한이 없고, 단일 속성으로 사용이 가능하다.

reactive는 반응성 변수가 많이 선언할 때 간단하게 사용이 가능해진다. 

 

 

 

 

 

참고블로그: https://geundung.dev/102

 

728x90

'Framework > vue_Architecture' 카테고리의 다른 글

Composition API: ref() vs reactive()  (0) 2023.01.02
Vue Router(1)  (0) 2023.01.02
fetching data (vue.js)  (0) 2022.12.26
부모, 자식간 데이터 전송 (vue.js)  (0) 2022.12.25
데이터 양방향 바인딩 (vue.js)  (0) 2022.12.25

댓글