Vue.js 란?
# Vue.js 란?
- JavaScript Framework 중 한 가지
- SPA 와 SFC 구조가 특징
- 개발 스타일은 Oprions API 와 Composition API 2가지가 있음
# VSCode Vue.js 프로젝트 설정
- 터미널에서 npm create vite@latest 입력하여 비트 설치
- vue, javascript 로 기본 설정
# VSCode extend 설치
- vue - official
- vue vscode snippets
# LifeCycle Hooks
- options API
- beforeCreated => option API 초기화 전 실행
- created => option API 초기화 후 실행
<template>
<div>count : {{ count }}</div>
<h1>Vue.js LifeCycle Test</h1>
</template>
<script>
export default {
data() {
return {
count: 0,
}
},
beforeCreate() {
console.log('beforCreated count : ', this.count)
},
created() {
console.log('created count : ', this.count)
this.test()
},
beforeMount() {
console.log('beforeMount h1 : ', document.querySelector('h1'))
},
mounted() {
console.log('mounted h1 : ', document.querySelector('h1'))
},
methods: {
test() {
console.log('test() 호출')
}
}
}
</script>
<style scoped></style>
# 선언적 렌더링 & 클래스/스타일 바인딩
<template>
<div>{{ html }}</div>
<div>{{ html2 }}</div>
<h1 v-html="html2"></h1>
<h2 v-bind:class="{ active: isActive }">클래스 바인딩 테스트</h2>
<h2 :class="{ active: isActive }">클래스 바인딩 테스트</h2>
<button @click="change">버튼</button>
<h3 style="color: red; font-size: 48px">스타일 바인딩 테스트</h3>
<h3 :style="{ color: fontColor, fontSize: fontSize + 'px' }">스타일 바인딩 테스트</h3>
</template>
<script>
export default {
data() {
return {
html: '이것은 텍스트 입니다.',
html2: '<span style="color:red"; >이것은 html 입니다.</span>',
isActive: false,
fontColor: 'red',
fontSize: 48,
}
},
mounted() {
console.log('mounted h1 : ', document.querySelector('h1'))
},
methods: {
change() {
this.isActive = !this.isActive
},
}
}
</script>
<style scoped>
h2.active {
color: green;
}
</style>
# v-if / v-else-if / v-else
<template>
<div v-if="isVisiable === true" class="red"></div>
<div v-if="isVisiable" class="blue"></div>
<div v-else class="black"></div>
<button @click="toggle">토글 버튼</button>
<div v-if="count === 1" class="red"></div>
<div v-else-if="count === 2" class="blue"></div>
<div v-else class="black"></div>
<button @click="count++">++</button>
<button @click="count--">--</button>
</template>
<script>
export default {
data() {
return {
isVisiable: true,
count: 1,
}
},
methods: {
toggle() {
this.isVisiable = !this.isVisiable
},
}
}
</script>
<style scoped>
.red {
width: 100px;
height: 100px;
background-color: red;
}
.blue {
width: 100px;
height: 100px;
background-color: blue;
}
.black {
width: 100px;
height: 100px;
background-color: black;
}
</style>
# v-show
- v-if 와 차이점: 동작 방식은 동일 But v-show는 태그가 렌더링이 되는데 v-if는 태그가 렌더링이 되지 않는다.
<template>
<div v-show="isVisiable === true" class="red"></div>
<div v-show="isVisiable === false" class="blue"></div>
<div v-if="isVisiable === true" class="black"></div>
</template>
<script>
export default {
data() {
return {
isVisiable: false,
}
},
}
</script>
<style scoped>
.red {
width: 100px;
height: 100px;
background-color: red;
}
.blue {
width: 100px;
height: 100px;
background-color: blue;
}
.black {
width: 100px;
height: 100px;
background-color: black;
}
</style>
# v-for
- key를 설정하는 것은 필수 (식별자를 통해 추후 렌더링 부하가 적어짐)
- index 값을 key로 설정하는 것은 권장하지 않음 (local data가 남아 있어 그 값을 참조 할 수 있음)
<template>
<div>
<!-- 기존 하드코딩 방식 -->
<span>기존 하드코딩 방식</span>
<li>{{ sampleArray[0] }}</li>
<li>{{ sampleArray[1] }}</li>
<li>{{ sampleArray[2] }}</li>
<li>{{ sampleArray[3] }}</li>
<!-- 배열 v-for 방식 -->
<span>배열 v-for 방식</span>
<li v-for="(item, index) in sampleArray" :key="item">{{ item }} / {{ index }}</li>
<!-- 객체 배열 v-for 방식 -->
<span>객체 배열 v-for 방식</span>
<li v-for="(item, index) in objectArray" :key="item.id">{{ item.id }} / {{ item.name }} / {{ index }}</li>
</div>
</template>
<script>
export default {
data() {
return {
sampleArray: ['a', 'b', 'c', 'd'],
objectArray: [
{ id: 0, name: 'kim' },
{ id: 1, name: 'lee' },
{ id: 2, name: 'park' },
{ id: 3, name: 'choi' },
]
}
}
}
</script>
<style lang="scss" scoped></style>
# computed
- computed의 가장 큰 특징은 캐싱임
<template>
<h1>{{ text }}</h1>
<h2> changeText 호출 값 {{ changeText() }}</h2>
<h2> changeText 호출 값 {{ changeText() }}</h2>
<h2> changeText 호출 값 {{ changeText() }}</h2>
<h3> computedText 호출 값 {{ computedText }}</h3>
<h3> computedText 호출 값 {{ computedText }}</h3>
<h3> computedText 호출 값 {{ computedText }}</h3>
</template>
<script>
export default {
data() {
return {
text: 'computed 테스트 문구'
}
},
methods: {
changeText() {
console.log('changeTex() 호출')
return this.text.split('').reverse().join('')
}
},
// computed의 가장 큰 특징은 캐싱 기능
computed: {
computedText() {
console.log('computedText 호출')
return this.text.split('').reverse().join('')
}
}
}
</script>
<style lang="scss" scoped></style>
# watch
- state등 변경을 감시할 때 사용
- watch 내부에서 state 값 변경은 권장 X
<template>
<span>{{ text }}</span>
<button @click="changeText">데이터 변경</button>
</template>
<script>
export default {
data() {
return {
text: '변경전 데이터 입니다.'
}
},
watch: {
text() {
alert('데이터가 변경되었습니다.')
}
},
methods: {
changeText() {
this.text = '변경 후 데이터 입니다.'
}
}
}
</script>
<style lang="scss" scoped></style>
# Options API Props
- 부모 컴포넌트에서 자식 컴포넌트 임포트시 componets 구조에 작성
- 자식 컴포넌트에서 부모 컴포넌트 Props을 props 구조에 작성
// 부모 컴포넌트
// ParentComponent.vue
<template>
<div>
<ChildComponent v-bind:props1="title" v-bind:props2="year" :props3="object"/>
</div>
</template>
<script>
import ChildComponent from './components/childComponent.vue';
export default {
components: {
ChildComponent,
},
data() {
return {
title: '부모 컴포넌트에서 보내는 데이터입니다.',
year: '2025',
object: {
name: 'park',
age: '28'
}
}
}
}
</script>
<style lang="scss" scoped></style>
// 자식 컴포넌트
// ChildComponent.vue
<template>
<div>{{ props1 }} </div>
<div>{{ props2 }} </div>
<div>{{ props3.name }} </div>
<div>{{ props3.age }} </div>
</template>
<script>
export default {
props: {
props1: String,
props2: Number,
props3: Object,
},
data() {
return {}
}
}
</script>
<style lang="scss" scoped></style>
# Composition API Props
- 원시타입은 ref, 배열 또는 객체 타입은 reactive로 사용하여 반응형으로 만듬 + import 문 추가
- 자식 컴포넌트에서 defineProps로 props 정의하여 사용
// 부모컴포넌트
// ParentComponent.vue
<template>
<div>
<ChildComponent v-bind:props1="title" v-bind:props2="year" :props3="object" />
</div>
</template>
<script setup lang="ts">
import { reactive, ref } from 'vue';
import ChildComponent from './components/childComponent.vue';
interface Obj {
name: string
age: number
}
const title = ref<String>('부모 컴포넌트에서 보내는 데이터입니다.')
const year = ref<Number>(2025)
const object = reactive<Obj>({
name: 'park',
age: 28
})
// ref로 설정된 경우 .value 사용
console.log(title.value)
console.log(year.value)
// reactive로 설정된 경우 속성에 직접 접근 가능
console.log(object.name)
console.log(object.age)
</script>
<style lang="scss" scoped></style>
<template>
<div>{{ props.props1 }} </div>
<div>{{ props.props2 }} </div>
<div>{{ props.props3.name }} </div>
<div>{{ props.props3.age }} </div>
<div>{{ props1 }} </div>
<div>{{ props2 }} </div>
<div>{{ props3.name }} </div>
<div>{{ props3.age }} </div>
</template>
<script setup lang="ts">
import { defineProps, toRef, toRefs } from 'vue';
interface Obj {
name: string
age: number
}
interface Props {
props1: String,
props2: Number,
props3: Obj,
}
const props = defineProps<Props>()
// const {props1, props2, props3} = toRefs(props)
// toRef으로 getter형식을 취하는 것이 좀 더 성능 개선
const props1 = toRef(props, 'props1')
const props2 = toRef(props, 'props2')
const props3 = toRef(props, 'props3')
</script>
<style lang="scss" scoped></style>
# Options API Emit
- 자식 컴포넌트에서 this.$emit('이벤트명', 데이터)를 통해서 버튼에 할당해서 부모 컴포넌트에 보냄
- 부모 컴포넌트에서 템플릿 자식컴포넌트에서 @이벤트명 을 통해서 parentEvent에 할당
<!-- 부모 컴포넌트 -->
<template>
<div>
부모 컴포넌트 레이아웃
<ChildComponent @send-event="parentEvent" />
</div>
</template>
<script>
import ChildComponent from './components/childComponent.vue';
export default {
components: {
ChildComponent
},
methods: {
parentEvent(event) {
console.log(event)
}
}
}
</script>
<style lang="scss" scoped></style>
<!-- 자식 컴포넌트 -->
<template>
<button @click="sendEvent">자식컴포넌트에서 만든 버튼</button>
</template>
<script>
export default {
data() {
return {
text: '자식 컴포넌트에서 선언된 데이터입니다.'
}
},
methods: {
sendEvent() {
this.$emit('send-event', this.text)
}
}
}
</script>
<style lang="scss" scoped></style>
# Composition API Emit
- 자식 컴포넌트에서 defineEmits와 emit을 사용하여 이벤트와 데이터를 부모 컴포넌트에 전달
- 부모 컴포넌트에서 @이벤트명 을 통해서 이벤트를 전달 받음
// 부모 컴포넌트
<template>
<div>
부모 컴포넌트 레이아웃
<ChildComponent @send-event="parentEvent" />
</div>
</template>
<script setup lang="ts">
import ChildComponent from './components/childComponent.vue';
const parentEvent = (event: string) => {
console.log(event)
}
</script>
<style lang="scss" scoped></style>
// 자식 컴포넌트
<template>
<button @click="sendEvent">자식컴포넌트에서 만든 버튼</button>
</template>
<script setup lang="ts">
import { ref, defineEmits } from 'vue';
const data = ref<string>('자식 컴포넌트에서 선언된 데이터입니다.')
const emit = defineEmits(['send-event'])
const sendEvent = () => {
emit('send-event', data.value)
}
</script>
<style lang="scss" scoped></style>
# v-model
<!-- v-model 과 한글을 input으로 입력받는 v-model 대체 @input -->
<template>
<div>
<input type="text" v-model="inputValue1">
<input type="text" :value="inputValue2" @input="inputValue2 = $event.target.value">
</div>
<div>{{ inputValue1 }}</div>
<div>{{ inputValue2 }}</div>
</template>
<script>
export default {
data() {
return {
inputValue1: '',
inputValue2: ''
}
}
}
</script>
<style lang="scss" scoped></style>