cyphen156

Vue3 핵심 문법-2 본문

웹/Vue

Vue3 핵심 문법-2

cyphen156 2023. 9. 6. 15:10

Watch와 WatchEffect

데이터 변화를 감지하여 콜백함수(함수안의 함수호출)을 가능하게 해주는 기능

사용법

watch('감시 대상 변수', (콜백함수))

watch함수 속성

  • immediate: DOM객체가 렌더링 되는 즉시 상태 변경을 감지(초기화를 기록함) // defalut는 false
  • deep: 감시 대상이 객체나 리스트 일때 그 안에 있는 내용까지 감시하기 위한 옵션(메모리 값을 감시하는것이기 때문에 객체나 리스트는 값이 변경되도 해당 변수의 위치 기록하고 있기 때문 ) // defalut는 false

watchEffect함수 속성

  • flush: 콜백 함수의 호출 시점을 정할 수 있는 속성, pre(DOM업데이트 전 호출) / post(DOM업데이트 후 호출)

예시(WatchWatchEffect.vue)

<template>
    <p>{{ count_0 }}</p>
    <button @click="count_0++">Options API 카운트 증가</button>
    <p>{{ count_1 }}</p>
    <button @click="count_1++">comoposition API 1st 카운트 증가</button>
    <p>{{ count_2 }}</p>
    <button @click="count_2++">composition API 2nd 카운트 증가</button>
    <p>{{ state }}</p>
    <button @click="onStop()">watchEffect 중지</button>
</template>

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

export default {
    //Options API
    data() {
        return {
            count_0: 0,
        }
    },
    watch: {
        // use fast arrow (not recommended)
        // count_0: (cur, prev) => {
        //     console.log('options API watch : ' + prev + '==>' + cur);
        // }, 

        // use function (ES6 recommended) 
        // watch나 watchEffect에서 arrow function을 사용하면 this의 컨텍스트가 상실될 수 있습니다. 
        // this가 undefined이거나 전역 객체에 바인딩될 수 있기 때문에 Vue는 이를 권장하지 않습니다.
        count_0: function(cur, prev) {
            console.log('options API watch : ' + prev + '==>' + cur);
        },
    },
    // Composition Api
    setup() {
        const count_1 = ref(0)
        const count_2 = ref(0)
        const state = ref('실행중')

        // 단일 데이터 감지
        watch(count_1, (cur, prev) => {
            console.log('Composition API watch : ' + prev + ' ==> ' + cur);
        }, {
            immediate: true,
        })
        // 다중 데이터 감지
        watch([count_1, count_2], (cur, prev) => {
            console.log('Composition API Multi watch : ' + prev + ' ==> ' + cur);
        })

        const stop = watchEffect(
            () => {
              console.log('Composition API watchEffect Called ' + count_2.value);
            },{
                flush: 'post',
            }
        )

        const onStop = () => {
          state.value = '중지';
          stop();
        }

        return {
            count_1,
            count_2,
            state,
            onStop,
        }
    }
}
</script>

컴포넌트 초기 상태(immidiate가 활성화 되어있는 count_1 변수로 인해 변수 값 초기화 시 watch가 상태 변화를 감지)

// 단일 데이터 감지
watch(count_1, (cur, prev) => {
    console.log('Composition API watch : ' + prev + ' ==> ' + cur);
}, {
    immediate: true,
})

Options API count_0++ 클릭시 이벤트 발생

<p>{{ count_0 }}</p>
<button @click="count_0++">Options API 카운트 증가</button>
//Options API
data() {
    return {
        count_0: 0,
    }
},
watch: {
    count_0: function(cur, prev) {
        console.log('options API watch : ' + prev + '==>' + cur);
    },
},

Composition API count_1++ 클릭시 이벤트 발생(2개의 watch함수 실행)

<p>{{ count_1 }}</p>
<button @click="count_1++">comoposition API 1st 카운트 증가</button>

Composition API count_2++ 클릭시 이벤트 발생(1개의 watch함수 실행)

<p>{{ count_2 }}</p>
<button @click="count_2++">composition API 2nd 카운트 증가</button>

Composition API onStop 클릭시 이벤트 발생(1개의 watchEffect함수 실행)

watchEffect를 중지시켰으므로  count_2++를 클릭해도 콘솔에 클릭당 하나의 로그만 찍히는 것을 볼 수 있다.

컴포넌트

애플리케이션을 만들 때 하나의 거대한 파일이 아닌 작은 부분 파일로 세분화하여 코드를 관리하는 시스템

중복되는 코드를 줄이고, 은닉화하여 유지 보수와 코드 재사용성을 효과적이게 만들어주는 객체지향적인 앱 설계이다.

예시)

main.js

import { createApp } from 'vue'
import App from './App.vue'

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

App.vue라는 거대한 하나의 앱 안에 세분화 되어있는 여러개의 컴포넌트

그리고 이 App.vue컴포넌트는 main.js에서 앱 인스턴스를 생성하고 index.html파일로 마운트 된다.

main.js에서는 전역 범위(앱 전체)에서 사용할 메서드와 변수들을 정의하고 사용할 수 있는 몇 가지의 애플리케이션 인스턴스 메서드가 존재한다. 자주 사용되거나 화면을 전환해도 데이터를 계속 유지해야 하는 경우에 사용하면 매우 효과적이다.

  • component: 컴포넌트를 전역 또는 로컬로 등록합니다.
  • config: Vue 앱의 전역 설정을 다룹니다.
  • directive: 커스텀 지시어를 정의합니다.
  • mixin: 여러 컴포넌트에서 공유될 수 있는 로직을 정의합니다.
  • mount: 컴포넌트를 DOM에 마운트합니다.
  • provide: 부모 컴포넌트에서 자식 컴포넌트로 데이터를 전달합니다.
  • unmount: 컴포넌트를 DOM에서 언마운트합니다.
  • use: Vue 앱에 플러그인을 추가합니다.

props

컴포넌트간의 데이터를 공유할 수 있는 사용자 지정 속성이다. react-native에서의 props와 비슷하다.

사용법

// 변수 형태의 props
export default {
  props: ['key', 'value'],
  // ...
}

// 객체 형태의 props
export default {
  props: {
    key: {
      type: String,
      required: true
    },
    value: {
      type: Number,
      default: 0
    }
  },
  // ...
}

 

부모 컴포넌트에서 자식 컴포넌트로 전달할 때

<template>
  <child-component :key="'myKey'" :value="10"></child-component>
</template>

전달받은 데이터를 자식컴포넌트가 사용할 때

<template>
  <div>
    Key: {{ key }}, Value: {{ value }}
  </div>
</template>

<script>
export default {
  props: ['key', 'value'],
  // ...
}
</script>

props의 옵션

  • type : 데이터 타입 지정
  • default : 부모 컴포넌트가 props를 전달하지 않았을 때의 기본값 지정
  • required : 부모 컴포넌트가 props를 반드시 전달해야 한다는 것을 의미한다. 만약 전달받지 못하면 경고를 띄운다.
  • validator : 데이터가 잘못 들어왔을 때 개발자 코드로 검사하여 콘솔창에 경고를 출력한다.

emits

props와는 반대로 자식 컴포넌트가 부모 컴포넌트에게 데이터를 전달하는 방법

데이터를 전달하는 자식 컴포넌트 코드

this.$emit('key', 'value')의 형태로 전달한다.

export default {
  emits: ['my-event'],
  methods: {
    onClick() {
      this.$emit('my-event', 'some value');
    }
  }
}

전달받는 부모 컴포넌트 코드

자식 컴포넌트 속성 : @customEvent="function"

<script>

  methods: {function(value) {

    //use emitted values

  }

}

</script>

<template>
  <child-component @my-event="handleMyEvent"></child-component>
</template>

<script>
export default {
  methods: {
    handleMyEvent(value) {
      console.log("Received value:", value);
    }
  }
}
</script>

Non-prop

컴포넌트에 전달되긴 하지만, props 또는 emits 옵션에 정의되지 않은(명시하지 않은) 컴포넌트의 속성을 의미한다.

ex) class, style, id ... ect

스크립트 코드에서 $attrs를 통해 접근한다

부모-자식 관계가 1세대 일때는 자동으로 부모컴포넌트의 속성을 자식 컴포넌트가 상속받지만 조상 - 부모 - 자식과 같이 세대가 여러 개일 때에는 속성을 상속하지 않는다.가령 부모 컴포넌트에서 생성하는 자식 컴포넌트를 가시적으로 표현하면 아래의 두 코드는 같다는 것이다.

<my_component class="non_prop">
  <template>
    <div>
    </div>
  </template>
</my_component>

 

<my_component class="non_prop">
  <template>
    <div class="non_prop>
    </div>
  </template>
</my_component>

여기서 script코드를 통해 div의 클래스 "non_prop"에 접근하고 싶다면 아래와 같이 두번째 인자로 context를 주면 된

export default {
  setup(props, context) {
    // context.attrs를 통해 attrs에 접근
    const nonPropClass = context.attrs.class;
    
    // 나머지 로직
  },
  // ... template or render function
};

non-prop세부 속성

inheritAttrs: 자식 노드에서 자동으로 상속받지 못하도록 하는 옵션

사용자 이벤트 생성

앞서서 emits 속성은 자식 컴포넌트가 데이터의 변경을 부모 컴포넌트에게 알리는 역할을 수행한다고 설명했다. 그런데  사실 이것은 v-model 과 같이 사용한다면 부모 뿐 아니라 다른 외부 컴포넌트로 자신이 가지고 있는 데이터의 변경을 전달하는, 양방향 통신을 가능하게 하기 때문에 사용자가 임의로 이벤트를 정의한다고 보는것이 더 적절하다.

Slots

웹 페이지를 구성할 때 hidden처리된 DOM을 shadow DOM이라고 부른다. 렌더링되지는 않았지만 분명 존재하는 컴포넌트를 사용할 때 Vue에서는 Slots라는 태그를 사용한다.

Slot Props(slot에 Non-Prop을 정의하기) : 컴포넌트에서 발생한 데이터를 slot에 결합시키는 방법

자식 컴포넌트 SlotProp.vue

// 자식 컴포넌트
<template>
    <ul style="list-style-type: none;">
      <li v-for="(item, i) in items" :key="i">
        <slot :item="item"></slot>
      </li>
    </ul>
  </template>
  
  <script>
  export default {
    data() {
      return {
        items: [1, 2, 3, 4, 5],
      };
    },
  };
  </script>

부모 컴포넌트 App.vue

<template>
  <SlotProp>
    <template v-slot="slotProps">
      {{ slotProps.item }}
    </template>  
  </SlotProp>
</template>

<script>
import SlotProp from './components/SlotProp.vue';

export default {
  name: 'App',
  components: {
    SlotProp
  }
}
</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>

또 다른 데이터 전송 방법

Provide/inject

부모 컴포넌트에서 provide: 속성을 통해 데이터를 자식 컴포넌트에게 제공하고,

자식 컴포넌트는 inject: 속성을 통해 전달받은 데이터를 사용한다.

Vuex

사용자 정의 디렉티브 ==> 커스터마이징 디렉티브 함수

사용법

app.directive('사용할 디렉티브 이름', {
	// 디렉티브 마운트
	mounted(요소) {
		요소.focus()
    }
    
    // 디렉티브 업데이트
    updated(요소) {
		요소.focus()
    }
})

// mounted와 updated가 모두 적용되는 디렉티브
app.directive('사용할 디렉티브 이름', => {
	요소.focus()
})

Mixins : 컴포넌트에 병합되어 기능을 확장하는 코드 조각"

한마디로 요약하자면 자주 사용되는 변수를 저장해놓는 객체다. 

현재는 컴포지션 함수와 Vuex가 거의 대부분의 기능을 대체하기 때문에 잘 사용하지는 않는다고 한다.

컴포넌트보다 먼저 호출된다 함은 A컴포넌트에서 B컴포넌트로 데이터를 전송할 때 일반적인 경우 B컴포넌트 틀을 구성하고, 변수를 선언한 뒤 여기에 받아온 데이터를 넣는데. 

그런데 Mixins를 사용하면 B컴포넌트 틀을 만들기 이전에 받아올 데이터를 저장하고 있다가 B컴포넌트를 구성하고, 변수의 초깃값을 이 받아온 데이터로 설정해버린다. 그래서 중복 변수명이 있을 경우 제대로 된 초기화가 안되는 치명적인 오류가 생길 수 있다.

 

이것으로 책에서 소개하는 Vue3의 핵심 문법에 대한 소개가 끝났다. 모든 문법을 전부 설명한 것은 아니기 때문에 필요할때마다 구글링해서 찾아쓰면된다.

 

모든 예제 코드의 소스파일은 

GitHub - dongprojectteam/vue3_examples

 

GitHub - dongprojectteam/vue3_examples

Contribute to dongprojectteam/vue3_examples development by creating an account on GitHub.

github.com

또는 제 깃허브 레포지토리에 존재합니다.

Workspace/Vue/project at main · cyphen156/Workspace · GitHub

 

 

 

 

 

' > Vue' 카테고리의 다른 글

Vue3 핵심 문법-1  (0) 2023.09.05
Vue3 프로젝트 뜯어보기  (0) 2023.09.01
Vue.js 입문하기  (0) 2023.09.01