Vue.js

Vue.js?

  • 간단한 화면 UI 개발 부터 라우팅, SSR 등의 애플리케이션 레벨의 개발을 지원하는 프레임워크
  • 리액트와 더불어 실무에서 가장 많이 사용되고 있는 프론트엔드 개발 라이브러리
  • 리엑트에 비해 진입 장벽이 낮고 쉽게 배울 수 있다.
  • 개발 생산성이 높고 JS 지식이 크게 요구 되진 않는다.
  • 프론트엔드, 백엔드 등 점차 직무적으로 전문화되고 있는 상황에서 처음 개발을 시작하는 프론트엔드 개발자 또는 백엔드 개발자에게 선호되는 경향

Vue2와 Vue3의 차이점

Vue3의 코드 작성 방식

옵션 API

<div id="app">{{ message }}</div>

<script>
	Vue.createApp({
		data(){
			return{
				message: 'hi',
			};
		},
	}).mount('#app');
</script>

컴포지션 API

<div id="app">{{ message }}</div>

<script>
	Vue.createApp({
		setup(){
			const message = ref('hi');
			
			return{
				message
			}
		}
	}).mount('#app')
</script>

개발환경 셋팅

Vue2

Pasted image 20231220100536.png

Vue3

Pasted image 20231220100654.png

간단 Vue 시작

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>\
<div id="app">
	   {{ message }}
</div>
<script>
	  Vue.createApp({
		  data() {
			  return {
				message: 'hi'
			}
		},
	}).mount('#app');
</script>

Reactivity

Pasted image 20231220103433.png

<div id="app"></div>

<script>
	const data = {
		a: 10
	}
	const app = new Proxy(data, {
		get(){
			console.log('값 접근')
		},
		set(){
			console.log('값 갱신')
		}
	})
</script>

! 400

Proxy

Proxy?

  • 객체의 기본 동작을 재정의 할 수 있다

간단하게 위의 코드를 화면에 뿌려주는 코드를 작성하겠습니다.

<div id="app">
    <!-- 해당 부분에서 렌더링 됩니다.
    -->
</div> 

<script>
    const data = {
        message: 10
    }
  
    function render(sth){
        const div = document.querySelector('#app');
        div.innerHTML = sth;
    }
  
    const app = new Proxy(data, {
        get(){
            console.log('hi')
        },
        set(target, prop, newValue){
            target[prop] = newValue
            console.log('값 갱신')
            render(newValue)
        }
    })
</script>

Parameters

Vue2 와 Vue3의 차이점

Pasted image 20231220133425.png

Data

문제점

기본적으로 Vue의 한계이기보다는 Object.defineProperty의 한계에 가까웠습니다.
이유는 아래와 같습니다.

var data = {
	a: 10
}

Object.defineProperty(data, 'a', {
	get(){
	},
	set(){
	},
});

위와 같이 Data를 가져오고 셋팅하기 위해서 Object.defineProperty를 사용합니다.
하지만 만약에 data라는 값이 a -> b로 바뀐다면?

var data = {
	a: 10,
	b: 10
}

Object.defineProperty(data, 'a', {
	get(){
	},
	set(){
	},
});

Object.defineProperty(data, 'b', {
	get(){
	},
	set(){
	},
});

위와 같이 모든 속성마다 Object.defineProperty를 선언해야합니다.
해당 불편한 점이 있지만, 위의 코드에서는 data 라는 객체를 물고 들어가기에, 좀 더 효율적으로 구성이 가능합니다.

Method

new Vue({
	el: ,
	template: ,
	data: ,
	methods: ,
	created: ,
	watch: ,
})
<div id="app">
    <p>{{count}}</p>
    <button v-on:click="addCount">+</button>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>

<script>
   Vue.createApp({
        data: function() {
            return{
                count: 0
            }
        },
        methods: {
            addCount(){
                this.count++;
            }
        },
   }).mount('#app')
</script>

Directive

<div id="app">
    <ul>
        <li v-for="item in items">
            {{item}}
        </li>
    </ul>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
   Vue.createApp({
        data: function() {
            return{
                items: ['삼성', '네이버', '인프런']
            }
        }
   }).mount('#app')
</script>

Vue Component

Pasted image 20231220145117.png

<!--html-->
<div id="app">
    <!-- 컴포넌트 표시 -->
    <app-header></app-header>
</div>

<!--javascript-->
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    Vue.createApp({
        // 인스턴스 옵션 속성 - 옵션 API
        components:{
            // '컴포넌트 이름' : 컴포넌트 내용
            'app-header' : {
                template: '<h1>컴포넌트 등록</h1>'
            }
        }
    }).mount('#app')
</script>

Component 통신 방식

Pasted image 20231220155632.png
데이터는 위에서 아래로 흐르고, 이벤트는 아래에서 위로 흐르게 됩니다.

상위 -> 하위

  • 프롭스 속성

하위 -> 상위

  • 이벤트 발생

Props 전달

<!--html-->
<div id="app">
    <!-- 컴포넌트 표시 -->
    <app-header></app-header>
</div>

<!--javascript-->
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    Vue.createApp({
        // 인스턴스 옵션 속성 - 옵션 API
        components:{
            // '컴포넌트 이름' : 컴포넌트 내용
            'app-header' : {
                template: '<h1>컴포넌트 등록</h1>',
                props: ['title']
            }
        }
    }).mount('#app')
</script>
Props 적용하기
<!--html-->
<div id="app">
    <!-- 컴포넌트 표시 -->
    <!-- <app-header v-bind:프롭스 이름="상위컴포넌트의 데이터 이름"></app-header> -->
    <app-header v-bind:title="appTitle"></app-header>
</div>

<!--javascript-->
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    Vue.createApp({
        // 인스턴스 옵션 속성 - 옵션 API
        data(){
            return{
                appTitle: '프롭스 넘기기'
            }
        },
        components:{
            // '컴포넌트 이름' : 컴포넌트 내용
            'app-header' : {
                template: '<h1>컴포넌트 등록</h1>',
                props: ['title']
            }
        }
    }).mount('#app')
</script>

Pasted image 20231220171015.png

이벤트 발생

<!--html-->
<div id="app">
    <!-- 컴포넌트 표시 -->
    <!-- <app-contents v-on:이벤트 이름="상위 컴포넌트의 메서드 이름"></app-contents> -->
    <app-contents v-on:refresh="showAlert"></app-contents>
</div>

<!--javascript-->

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    const appContents = {
        template: `
            <p>
                <button v-on:click="sendEvent">갱신</button>
            </p>
        `,
        methods: {
            sendEvent(){
                this.$emit('refresh');
            }
        },
    }
    // Root 컴포넌트
    Vue.createApp({
        methods:{
            showAlert(){
                alert('새로고침')
            }
        },
        // 인스턴스 옵션 속성 - 옵션 API
        components:{
            // '컴포넌트 이름' : 컴포넌트 내용
            'app-contents' : appContents
        }
    }).mount('#app')
</script>

동일 레벨 컴포넌트 데이터 전달

<!-- html -->
<div id="app">
	<!--<app-header v-bind:appTitle="message"></app-header>-->
	<!-- HTML에서는 카멜케이스를 구분 하지 못하므로 하이픈을 넣은 것일뿐 .vue 파일은 구분 가능하니 이부분은 참고만 하면 된다.-->
    <app-header v-bind:app-title="message"></app-header>
    <app-contents v-on:login="receive"></app-contents>
</div>
  
<!-- javascript -->

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    const appHeader = {
        props: [ 'appTitle' ],
        template: '<h1>{{ appTitle }}</h1>'
    }
  
    const appContents = {
        template: `
            <p>
                <button v-on:click="sendEvent">로그인</button>
            </p>
        `,
        methods: {
            sendEvent(){
                this.$emit('login');
            }
        },
    }
    // Root 컴포넌트
    Vue.createApp({
        data(){
            return {
                message: ''
            }
        },
        methods:{
            receive(){
                console.log('받았다.')
                this.message = '로그인됨'
            }
        },
        components: {
            // '컴포넌트 이름': 컴포넌트 내용
            'app-header' : appHeader,
            'app-contents' : appContents
        }
    }).mount('#app');
</script>

Vue Template 문법

v-if & v-show

<!-- HTML -->
<div id="app">
    <!-- v-if -->
    <p v-if="login">로그인 되었습니다.</p>
    <p v-else>로그인 하세요</p>
    <button v-on:click="loginUser">로그인</button>
  
    <hr>

    <!-- v-show -->
    <p v-show="login">로그인 되었습니다.</p>
    <button v-on:click="loginUser">로그인</button>
</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    Vue.createApp({
        data(){
            return{
                login: false
            }
        },
        methods: {
            loginUser(){
                this.login = !this.login;
            }
        },
    }).mount('#app');
</script>

v-if & v-show 차이점

class & id 바인딩

<style>
    .primary{
        color: coral;
    }
</style>
  
<div id="app">
<!-- HTML -->
    <h1>클래스 바인딩</h1>
    <div v-bind:class="textClass">데이터 바인딩 예제</div>
  
    <!-- id 바인딩 -->
    <h1>아이디 바인딩</h1>
    <section v-bind:id="sectionId" :style="sectionStyle">
        반갑습니다.
    </section>
</div>

<!-- Javacript -->
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    Vue.createApp({
        data() {
            return {
                textClass: 'primary',
                sectionId: 'tab1',
                sectionStyle: { color: 'red' }
            }
        },
    }).mount('#app');
</script>

Pasted image 20231221102931.png

Vue CLI

npm install -g @vue/cli

vue create vue3-cli

Project 폴더 내용

페이지 로딩 과정

!Pasted image 20231221161842.png
Vue 프로젝트를 시작 하였을 때, 위의 script 태그를 작성합니다.
그렇게 되면, Network 창을 눌러보게 된다면, 아래의 사진에 Network 에서 보이게 됩니다.
Pasted image 20231221162418.png

Vue Single File Component

Single File Componet?

  • HTML, CSS, JS 코드를 한 파일에서 관리하는 방법 입니다.
  • Vue CLI로 프로젝트를 생성하고 나면, App.vue 라는 파일을 확인 할 수 있습니다.
  • 이처럼 vue 확장자를 가진 파일을 모두 싱글 파일 컴포넌트라고 합니다.

<!-- .vue 파일 구조 -->
<template>
	<!-- HTM: (뷰 컴포넌트의 표현단, 템플릿 문법) -->
</template>

<script>
	<!-- HTM: (뷰 컴포넌트의 표현단, 템플릿 문법) -->
</script>

<style>
	<!-- HTM: (뷰 컴포넌트의 표현단, 템플릿 문법) -->
</style>

실습

<template>
  <form action="" @submit.prevent="submitForm">
    <div>
      <label for="userId">ID : </label>
      <input type="text" id="userId" v-model="username">
    </div>
    <div>
      <label for="password">PW : </label>
      <input type="text" id="password" v-model="password">
    </div>
    <button type="submit">로그인</button>
  </form>
</template>

<script>
import axios from 'axios'

export default {
  data(){
    return{
      username: '',
      password: ''
    }
  },
  methods:{
    submitForm(){
      // event.preventDefault(); // evnet를 작동시켜도 input이 사라지지 않는 속성이다.
      axios.post('https://jsonplaceholder.typicode.com/users/',{
        username: this.username,
        password: this.password
      })
      .then(response =>{
        console.log(response)
      })
      console.log('눌림')
    }
  }
}
</script>

<style>
</style>

App.vue

Json PlaceHoder


#FrontEnd #Vue #JavaScript #Vue3