cyphen156
Vue3 핵심 문법-2 본문
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
또는 제 깃허브 레포지토리에 존재합니다.
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 |