vue3-study

project npm script

- Project setup
npm install

- Compiles and hot-reloads for development
npm run serve

- Compiles and minifies for production
npm run build

- Lints and fixes files
npm run lint

create velog post(korean)

project setting history

npm i -g @vue/cli
vue -V
vue create <project-name>

생성된 vue 프로젝트 폴더로 이동
vue add 명령어를 이용하여 생성된 프로젝트에 플러그인 설치

vue add vue-next
https://github.com/vuejs/vue-cli-plugin-vue-next

플러그인 설치를 하면, package.json 파일 내용을 살펴보면 몇가지 라이브러리에 beta 라고 붙은것들이 존재합니다.
저는 일단 수동으로 alpha 로 변경하였습니다.

뭐가 달라졌을까?🧐

  • src/main.js
import { createApp } from 'vue';
import App from './App.vue';

createApp(App).mount('#app');

main.js 에서 Vue 생성자 함수를 사용하는게 아닌, createApp 함수를 사용하여 초기화 한다.

  • src/components/HelloWorld2.vue
<template>
    <!-- 더 이상 root Element 로 감싸지 않아도 된다. -->
    <h1>{{ msg }}</h1>
    <h2>나는 헬로월드 2 이지렁</h2>
</template>

기존에는 template 에 root element 로 감싸야 했었으나, 이제는 필요가 없다. (Fragment)

  • src/components/HelloWorld2.vue
<template>
    <!-- 더 이상 root Element 로 감싸지 않아도 된다. -->
    <h1>{{ msg }}</h1>
    <h2>나는 헬로월드 2 이지렁</h2>
    <h3>{{helloMessage}}</h3>
    <hr/>
    <div>
        {{ testMessage }}
        <button @click="changeTestMessage('change!!')"> testMessageChange </button>
    </div>
    <div>
        {{ testMessage2.msg }}
        <button @click="changeTestMessage2('변경이 되었습니다!!')"> testMessageChange2 </button>
    </div>
</template>

<script>

    import { ref } from 'vue'

    const changeTestMessageRef = () => {
        // ref 객체에는 value 속성을 가지고 있음.
        // Takes an inner value and returns a reactive and mutable ref object. => 반응하고 변경가능한 ref 객체를 반환
        // 내부값은 .value 속성을 의미한다.
        const testMessage = ref('이히히히힝!');

        const changeTestMessage = (targetValue) => {
            testMessage.value = targetValue;
        };

        return [testMessage, changeTestMessage];
    };

    const changeTestMessageRef2 = () => {
        // ref 는 원시타입 및 객체에 해당하는 내부값을 가지는 ref 객체 생성 가능
        const testMessage2 = ref({msg : '아햏햏햏햏!'});

        const changeTestMessage2 = (targetValue) => {
            testMessage2.value.msg = targetValue;
        };

        return [testMessage2, changeTestMessage2];
    };

    export default {
        name: 'HelloWorld2',
        props: {
            msg: String
        },

        // The setup function is a new component option
        // Composition API 를 사용하기 위한 진입점 역할(컴포넌트 내에서)
        setup() {

            const [testMessage, changeTestMessage] = changeTestMessageRef();
            const [testMessage2, changeTestMessage2] = changeTestMessageRef2();
            const helloMessage = 'hello composition API';

            setTimeout(() => {
                helloMessage = 'setTimeout change;';
                console.log('setTimeout call..');
            }, 1000); // 1초후에 helloMessage 값을 변경

            // expose to template
            return {
                testMessage, // .value 할 필요가 없다, 자동으로 참조
                changeTestMessage,
                testMessage2,
                changeTestMessage2,
                helloMessage, // 값 변경 시 DOM 에 반영되지는 않는다(not reactive)
            }
        },
    }
</script>

composition API 를 통한 반응형 데이터 정의(ref)

  • vue 2 기존
<template>
    <div>
        {{msg}}
    </div>
</template>

<script>
    export default {
        data() {
            return {
                msg : 'vue2',
            }
        }  
    }
</script>

  • computed : 변경 불가능한 ref 객체를 반환받는다.
import { ref, computed } from 'vue';
const computedExample = () => {
      const testNumber = ref(1);
      const testNumber2 = ref(2);

      // read-only
      const printNumber = computed(() => testNumber.value);

      console.log(printNumber.value);
      printNumber.value++; //error : Write operation failed: computed value is readonly

      // get, set 함수를 구현하여 사용이 가능
      // writable
      const printNumber2 = computed({
          get : () => testNumber2.value,
          set : value => testNumber2.value = value
      });
      printNumber2.value = 100; // set 함수를 구현하면 쓰기 가능
      console.log(printNumber2.value);

};
  • src/components/HelloWorld3.vue
<template>
    <div>
        <h1>
            {{reactiveStates.text}}
        </h1>
        <p style="font-size: 17px">
            count : {{reactiveStates.count}}
        </p>
        <div>
            <button style="margin: 4px; padding: 2px" @click="reactiveStates.count++">
                add count(reactive)
            </button>
            <button style="margin: 4px; padding: 2px" @click="countIncrease">
                add count(ref)
            </button>
        </div>
        <template v-for="(value, index) in reactiveStateArray">
            <p style="padding: 2px; margin: 0" :key="index">
                {{value}}
            </p>
        </template>
        <hr/>
        <template v-for="(value, index) in reactiveStateArray2">
            <p style="padding: 2px; margin: 0" :key="index + 100">
                {{value}}
            </p>
        </template>
        <div>
            -- readonly
            <p>
                original.count : {{original.count}}
            </p>
            <button @click="original.count++">add original count</button>
            <p>
                originalReadonly.count : {{originalReadonly.count}}<br>
                f12 를 눌러, 크롬 개발자 도구를 확인해보세요!
            </p>
        </div>
    </div>
</template>

<script>

    import {reactive, ref, readonly, watchEffect} from 'vue';

    const countRef = () => {
        const count = ref(0);

        const countIncrease = () => count.value++;

        return [count, countIncrease];
    };

    export default {
        name: "HelloWorld3",

        setup() {

            const [count, countIncrease] = countRef();

            // reactive state
            // 객체를 받고, 반응 프록시 객체를 반환(=== Vue.observable() )
            // 이 상태변화 감지는 deep 하게 동작
            const reactiveStates = reactive({
                text: 'hello world3!',
                count, // ref 객체를 reactive object 속성으로 사용이 가능하며, 자동으로 count.value 값을 참조(그래서, reactiveStates.count 로 참조 가능)
            });

            // reactive state -> array
            const reactiveStateArray = reactive([]);
            reactiveStateArray.push(0);
            reactiveStateArray.push(1);
            reactiveStateArray.push(2);

            // 만약 배열요소(컬렉션 요소)에 ref 객체가 있다면 unwrapping 되지 않음(자동으로 .value 참조가 되지 않음)
            const reactiveStateArray2 = reactive([ref('a'), ref('b')]);
            reactiveStateArray2.push('c');
            // console.log(reactiveStateArray2);


            // readonly
            const original = reactive({count: 0});
            // 읽기전용 프록시 원본 객체 반환(중첩된 참조도 전부 읽기전용)
            // 원본 reactive 객체에 값이 변경되면, 읽기전용 프록시 원본객체도 변경이 이루어짐.
            const originalReadonly = readonly(original);

            // 초기 1번 즉시 함수를 즉시실행하고, 종속된 상태값이 변경될 때 마다 다시 실행
            watchEffect(() => {
                // works for reactivity tracking
                console.log(originalReadonly.count);
            });

            // mutating the copy will fail and result in a warning
            originalReadonly.count++; // warning! -> Set operation on key "count" failed: target is readonly

            return {
                reactiveStates,
                countIncrease,
                reactiveStateArray,
                reactiveStateArray2,
                original,
                originalReadonly,
            }
        },
    }
</script>

composition API 를 통한 반응형 데이터 정의(reactive) readonly : 읽기전용 프록시 원본 객체 반환(그래서, 원본객체가 변경이 되면 읽기전용 객체도 변경이 이루어짐) watchEffect : 1번 즉시 실행 후, 종속된 상태값이 변경될 때 마다 다시 실행(반응성 추적)

  • 반응형 상태(reactive data) 정의는 ref, reactive 를 활용

  • ref 는 원시타입 및 객체도 받으며, reactive 는 객체만 받음

  • isRef(), isReactive() 메서드는 반응형 객체가 ref 타입인지 reactive 타입인지 검사를 한 후 boolean 반환(src/App.vue 참조)

  • watch 와 watchEffect 차이는 초기에 즉시실행이 되냐 안되냐 여부 (src/components/HelloWatch.vue 참조)

  • vue 3 life cycle (on 이라는 접두사를 사용하는 형태로 바뀌고, creation 생명주기가 setup() 으로 대체)

beforeCreate -> use setup()
created -> use setup()
beforeMount -> onBeforeMount
mounted -> onMounted
beforeUpdate -> onBeforeUpdate
updated -> onUpdated
beforeDestroy -> onBeforeUnmount
destroyed -> onUnmounted
errorCaptured -> onErrorCaptured
lifecycle

  • vue2 에서 크게 생명주기는 변화지 않았음
  • vue3 에서 생명주기 함수들은 on 이라는 접두사로 시작
  • Vue2OptionsAPI vs Vue3CompositionAPI 컴포넌트를 통해 비교 가능
참조 : https://css-tricks.com/an-early-look-at-the-vue-3-composition-api-in-the-wild/
composition API : 컴포넌트 로직을 유연하게 구성할 수 있는 API 모음

- Vue2 컴포넌트가 커질수록 가독성이 어려워짐(Options API 의 구조가 복잡해짐 => data(), computed, watch, methods 로 분할되었으나 커지면 커질수록 명확한 구분이 없어지고 기능의 일부가 포함이 됨)
- 재사용시 단점이 존재(mixin 이라는 좋은 방법이 있으나, 이름 충돌 및 병합 이슈 등이 있음)

vuex 4 beta 2 버전 맛보기

참조 : https://unpkg.com/browse/vuex@4.0.0-beta.2/
참조 : https://github.com/u3u/vue-hooks
참조 : https://forum.vuejs.org/t/vuex4-usestore-returns-undefined-in-some-cases/96026
npm install --save vuex@4.0.0-beta.2

- 아래 경로 및 파일 참조
/src/components/HelloStore.vue
/store/*
/src/main.js

Customize configuration

See Configuration Reference.