IT/Vue.js

Vue.js 란?

벼락쟁이 2025. 3. 17. 08:32

# 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>