똑같은 삽질은 2번 하지 말자

Vue3(Composition API) 가 되면서 바뀌는 점 본문

Vue

Vue3(Composition API) 가 되면서 바뀌는 점

곽빵 2021. 6. 6. 19:56

Fragment

template안에 무조건 하나의 루트태그만 허용되었는데, 이제는 복수여도 상관없다.

Vue3 Preview default Project로 생성했을때, App.vue 상태 

 

data  => ref or reactive

ref vs reactive

ref -> 모두 사용가능

reactive -> Array, Object, Map, Set

 

reactive에 대해서는 좀더 다루고 싶으나 toRefs를 다룰때 좀 더 자세히 기술할 예정

 

접근방법

value로 접근하냐 안하냐의 차이이다,(template에서는 그냥 접근가능)

<template>
  <div class="name">
    {{ greeting(name) }}
  </div>
  <div class="name">
    {{ greeting(names.name) }}
  </div>
  <button class="btn btn-primary" @click="updateName()">Button</button>
</template>

<script>
import { ref, reactive } from "vue";
export default {
  setup() {
    const name = ref("Gwak hee won"); // number array object string 다 가능
    const names = reactive({
      id: 1,
      name: "1번",
    });

    const greeting = (name) => {
      return "Hello" + name;
    };

    const updateName = () => {
      name.value = name.value + "2";
      names.name = names.name + "2";
      console.log(name);
    };

    return {
      name,
      names,
      greeting,
      updateName,
    };
  },
};
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

Props , Emit

props emit은 별 그렇게 차이는 없어보인다.

좀 눈에 띄는게 있다면 emit을 한 군데에 모아두는 곳이 생겼다.

export default {
  props: {
    todos: {
      type: Array,
      required: true,
    },
  },
  emits: ["toggle-todo", "delete-todo"],
  setup(props, { emit }) { // emit is in context
    const toggleTodo = (index) => {
      emit("toggle-todo", index);
    };

    const deleteTodo = (index) => {
      emit("delete-todo", index);
    };
    return {
      toggleTodo,
      deleteTodo,
    };
  },
};

setup Function의 파라미터로써 props emit(contenxt 의 distructuring)을 받고있으며,

props는 props.props명으로 접근할 때 쓸려고 선언한다.

 

Computed 

Vue에서 강력하다고 생각하는 기능중 하나인 computed는 어떻게 바뀌었을까

import { ref, computed } from "vue";

우선 이렇게 선언이 필요

const douvleCountComputed = computed(() => {
  console.log("computed gwak");
  return count.value * 2;
});

;

이렇게 메소드하고 비슷하지만, computed로 감싸는게 다르다.

어라 그럼 메소드를 쓰는것과 무슨차이가 있을까? 라고 물어본다면

1. computed 는 parameter를 받을수 없다.

2. computed cash기능이 있으므로 내부의 reative state가 있을때! 그리고 그 친구가 변경될때 실행

(methods는 무조건 실행한다.(부르는 순간) but Computed는 값을 캐시함으로써 state가 변경이 없으면 캐시값을 반환)

 

const douvleCountComputed = computed(() => {
   console.log("computed gwak");
   return count.value * 2;
});

const douvleCountMethod = (name) => {
   name = "method gwak";
   console.log(name);
   return count.value * 2;
};

});

 

computed는 한번만 실행

watch, watchEffect

watctEffect ?

듣도보도 못한 훅이 한개 생겨났다.

내부의 리액티브값이 변경되면 실행되는 친구이다.

watchEffect(() => {
  console.log(numberOfPages.value); // numberOfPages가 변경시 출력
});

공식 문서(watcheffect)

Vue에서 반응형 상태의 필수 사용 사례는 렌더링 중에 사용할 수 있다는 것입니다.

종속성 추적 덕분에 반응형 사태가 변경 될 때 뷰가 자동으로 업데이트됩니다.

DOM에서 무언가를 렌더링하는 것은 "부수효과"로 우리의 프로그램은 프로그램 자체(DOM) 외부의 상태를 수정하고 있습니다.

반응형 상태에 따라 부수효과를 적용하고 자동으로 다시 적용하려면 watchEffect API를 사용할 수 있습니다.

import { reactive, watchEffect } from 'vue' 

const state = reactive({ count: 0 }) 
watchEffect(() => { document.body.innerHTML = `count is ${state.count}` })

watchEffect 는 원하는 부수효과를 적용하는 함수를 기대합니다 (이 경우 innerHTML 설정).

함수를 즉시 실행하고 실행 중에 사용한 모든 반응 상태 속성을 타겟으로 추적합니다.

여기서 state.count 는 초기 실행 후 감시자에 대한 타겟으로 추적되며, 앞으로 state.count 가 변경되면 내부 함수가 다시 실행됩니다.

이것이 Vue의 반응형 시스템의 핵심입니다. 컴포넌트의 data()에서 객체를 반환하면 내부적으로 reactive()에 의해 반응이 이루어집니다. 템플릿은 이러한 반응형 속성을 사용하는 렌더링 함수 (보다 효율적인 innerHTML로 생각)로 컴파일 됩니다.

 

watchEffect 는 2.x watch 옵션과 유사하지만 감시된 데이터 소스와 부수효과 콜백을 분리할 필요가 없습니다.

Composition API는 2.x 옵션과 정확히 동일한 동작을 하는 watch 기능을 제공합니다.

 

이런식으로 리액트? 처럼 구성하는것도 가능

import { reactive, computed, watchEffect } from 'vue'

function setup() {
  const state = reactive({
    count: 0,
    double: computed(() => state.count * 2)
  })

  function increment() {
    state.count++
  }

  return {
    state,
    increment
  }
}

const renderContext = setup()

watchEffect(() => {
  renderTemplate(
    `<button @click="increment">
      Count is: {{ state.count }}, double is: {{ state.double }}
    </button>`,
    renderContext
  )
})

 

watch

watch는 글쎄 그닥 바뀐건 없어보인다. watch는 watch인데 watch내에서 복수의 리액티브값들을 관리하는게 가능해졌다 한다.

const a = reactive({
  b: 1,
  c: 3,
});

watch(
  () => [a.b, a.c],
  (current, prev) => {
    console.log(current, prev);
  }
);
a.b = 2; // 2 1
a.c = 2; // 3 2 

객체 배열의 깊은 감시를 위한 watch usage

    const state = reactive({
      editTodoList: [] as EditTodo[],
    })

    watch(
      () => state.editTodoList,
      (cur: EditTodo[], prev: EditTodo[]) => {
        console.log(cur, prev)
        emit('updateTodoList', cur)
      },
      { deep: true },
    )

 

LifeCycle Hooks

 

Because setup is run around the beforeCreate and created lifecycle hooks, you do not need to explicitly define them. In other words, any code that would be written inside those hooks should be written directly in the setup function.

 

beforaCreate, created = setup() 이라는 말 

 

 

Teleport

하나의 컴포넌트나 태그자체를 어딘가로 아예 옮기는게 가능한 기술이 새로 등장했다.

(보통 모달이나 confirm message같은 친구들한테 쓰면 유용할꺼 같다.)

 

어딘가의 컴포넌트.vue

<teleport to="#deleteModal">
  <Modal v-show="showModal" @delete="deleteTodo" @close="closeModal"></Modal>
</teleport>

index.html(body)

<body>
  <noscript>
    <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled.
        Please enable it to continue.</strong>
  </noscript>
  <div id="app"></div>
  <div id="deleteModal"></div>
  <!-- built files will be auto injected -->
</body>

app 태그 밑에 붙은 모달

 

v-model

Vue2에서、부모 자식 컴포넌트의 양방향 바인딩으로 .sync

가 사용되어지고 있는데


Vue3부터는 .sync가 없어지고 v-model:hoge로 바뀐다

 

다수의 v-model 예시

<ChildComponent v-model:title="pageTitle" v-model:content="pageContent" />

<!-- would be shorthand for: -->

<ChildComponent
  :title="pageTitle"
  @update:title="pageTitle = $event"
  :content="pageContent"
  @update:content="pageContent = $event"
/>

 

만약에 단 하나의 v-model만 연결한다면, :v-model:title에서 :title을 생략하고, 자식에서는 modelValue로 props를 받으면 된다.

예시

 

부모.vue

<div class="col-6">
  <Input label="Subject" :error="subjectError" v-modal="todo.subject" />
</div>

자식.vue

<template>
  <div class="form-group">
    <input :value="subject" @input="onInput" type="text" class="form-control" />
  </div>
</template>

<script>
export default {
  props: {
    modelValue: {
      type: String,
      required: true,
    },
  },
  setup(props, context) {
    const onInput = (e) => {
      context.emit("update:modelValue", e.target.value); // update:props이름(v-model로 바인딩한)
    };

    return {
      onInput,
    };
  },
};
</script>

 

ToRefs

일반 객체를 반응객체로 전환시키기 위해 등장한 친구이다. 우리가 reactive로 감싸서 여러 요소들을 동시에 반응형으로 만들수 있지만,
개네들이 한번 풀어져서(distruturing) 되거나 그러면 반응성을 잃어버리는 경우가 있다. 

import { reactive } from "vue"

export const useCount = () => {
  const state = reactive({
    count: 0
  })
  return state;
}

 

<template>
  <div>Home Page</div>
  <div>{{ count }}</div>
  <button @click="count++">Add</button>
</template>

<script>
import { useCount } from "@/composables/count";
export default {
  setup() {
    const { count } = useCount(); // distructuring되는 순간 반응형객체가 아니게 된다.
    console.log(count);
    return {
      count,
    };
  },
};
</script>

Proxy로 안되어있는 그냥 primitive number 0

그럼 하나하나 반응형을 씌워서 export하면 된다.

import { reactive, toRefs } from "vue"

export const useCount = () => {
  const state = reactive({
    count: 0
  })
  return toRefs(state);
}

반응형이 씌워진 count

useContext

setup 함수의 두번째 파라미터로 오는 context를 useContext로도 접근할 수 있다.

우리가 setup에 첫번째 인자로오는 props를 안쓰고 emit은 쓰는 경우에는 setup에서 파라미터를 받지말고

useContext로 바로 emit을 밑의 사진처럼 가져와서 쓸 수 있다.

vue2 의 composition api 에서는 context안에 좀 더 많은 내용들이 담겨있었는데, 없어진 친구들이 있는것 같다.

root(root.$set, root.$delete) 는 일단 없어졌다...

물론 vue3에서는 set, delete를 쓰지 않아도 Reference type들도 reactive하게 되었기 때문에 크게 신경쓸 필요는 없다.

 

꽤 알기 쉽게적어놓은 일본 아티클

https://blog.capilano-fw.com/?p=6393 

 

Vue 3の新しい機能と変更点・全11件 – console dot log

こんにちは❗フリーランス・エンジニアの 九保すこひ です。 さてさて、前回「Bootstrap v5がどうなるのか調査してみた」という記事を公開しましたが、実は今後のリリースでもうひとつ気に

blog.capilano-fw.com

 

https://ake.kr/2020/10/01/vue3-life-starting-with-the-vite/#vue-3

 

Comments