Vue3 기본문법

1. App.vue

<script setup lang="ts">
</script>

<template>
  <h1>Hello Vue 3</h1>
</template>

<style scoped>
</style>

 

 

- script setup -> Vue3 권장 문법

- template -> 화면

- style scoped -> 이 컴포넌트에만 스타일 적용

 

2. 반응형 데이터 (ref)

<script setup lang="ts">
import { ref } from "vue";

const count = ref(0);
</script>

<template>
  <h1>Count: {{ count }}</h1>
</template>

 

- ref(값) -> 반응형

- template에서는 .value 안씀

 

3. 이벤트 바인딩 (click)

<script setup lang="ts">
import { ref } from "vue";

const count = ref(0);

function increment(){
  count.value++;
}

</script>

<template>
  <h1>Count: {{ count }}</h1>
  <button @click="increment" >+</button>
</template>

 

- @click = v-on:click

- script에서는 .value로 값을 뽑음

- 버튼 클릭시 count를 1씩 증가

 

 

4. v-model (양방향 바인딩)

<script setup lang="ts">
import { ref } from "vue";

const name = ref("");
</script>

<template>
  <input v-model="name" placeholder="이름 입력" />
  <p>안녕하세요, {{ name }}</p>
</template>

 

- v-model = input <-> state 자동 연결

- form에서 필수 문법

 

 

5. 조건 렌더링 (v-if)

<script setup lang="ts">
import { ref } from "vue";

const isLogin = ref(false);
</script>

<template>
  <button @click="isLogin = !isLogin">
    로그인 토글
  </button>

  <p v-if="isLogin">로그인 상태</p>
  <p v-else>로그아웃 상태</p>
</template>

 

- 기본값 false이므로 처음 화면은 로그아웃 상태, 버튼 클릭시 로그인 상태로 변경됨

- 조간이 자주 바뀌면 v-show 고려, 

 

6. reactive vs ref

- ref : 원시값 (number, string, boolean)

- reactive : 객체, 폼데이터

<script setup lang="ts">
import { reactive, ref } from "vue";

const count = ref(0);

const user = reactive({
  name: "",
  age: 0,
})
</script>

<template>
  <p>count: {{ count }}</p>

  <input v-model="user.name" placeholder="이름" />
  <input v-model="user.age" type="number" placeholder="이름" />

  <p>{{ user.name }} // {{ user.age }}</p>
</template>

 

- reactive는 .value 못씀

- 객체는 왠만하면 reactive

 

7. computed

<script setup lang="ts">
import { computed, ref } from "vue";

const count = ref(0);

const doubleCount = computed(() => {
  return count.value * 2;
})

</script>

<template>
  <button @click="count++"></button>
  <p>count: {{ count }}</p>
  <p>double: {{ doubleCount }}</p>
</template>

- 값 가공은 computed

- computed는 캐싱효과가 있음

 

7-1. computed 캐싱

<script setup lang="ts">
import { computed, ref } from "vue";

const count = ref(0);

const doubleCount = computed(() => {
  console.log("computed")
  return count.value * 2;
})

const test = () => {
  console.log("method");
  return count.value * 2;
}

</script>

<template>
  <button @click="count++"></button>
  <p>count: {{ count }}</p>
  <p>double: {{ doubleCount }}</p>
  <p>double: {{ doubleCount }}</p>
  <p>double: {{ test() }}</p>
  <p>double: {{ test() }}</p>
</template>

 

- 위에 코드를 실행후 network console로 확인을하면 computed로 선언한 함수는 2번 호출했는데 1번이 console로 찍히고,

함수를 2번 호출시는 2번 console이 찍힘

 

 

 

8. Watch

<script setup lang="ts">
import { ref, watch } from "vue";

const count = ref(0);

watch(count, (newVal, oldVal) => {
  console.log("변경 : ", oldVal, "->", newVal);
})

</script>

<template>
  <button @click="count++"></button>
  <p>{{ count }}</p>
</template>

 

- count 값이 변경될때 watch가 동작

 

9. v-for

<script setup lang="ts">
import { ref } from "vue";

const todos = ref([
  { id: 1, text: "Vue 공부" },
  { id: 2, text: "TypeScript 공부" },
]);
</script>

<template>
  <ul>
    <li v-for="todo in todos" :key="todo.id">
      {{ todo.text }}
    </li>
  </ul>
</template>

 

- key 필수, id 없으면 에러

 

9-1. computed + v-for

<script setup lang="ts">
import { ref, computed } from "vue";

const todos = ref([
  { id: 1, text: "Vue", done: true },
  { id: 2, text: "TS", done: false },
]);

const doneTodos = computed(() =>
  todos.value.filter(t => t.done)
);
</script>

<template>
  <h3>완료</h3>
  <ul>
    <li v-for="t in doneTodos" :key="t.id">
      {{ t.text }}
    </li>
  </ul>
</template>

- todo 리스트에서 done이 true인 항목만 필터링해서 화면에 뿌림

 

10. props & emit

Parent.vue

<script setup>
import { ref } from 'vue'
import Child from './Child.vue' // 자식 컴포넌트 불러오기

const myTitle = ref("부모가 주는 선물")
const myCount = ref(0)

// 자식이 보낸 신호를 받았을 때 실행할 함수
const handleIncrease = () => {
  myCount.value++
  console.log("자식의 신호를 받았습니다!")
}
</script>

<template>
  <div style="border: 2px solid #35495e; padding: 20px;">
    <h1>👨‍👩‍👧 여기는 부모 컴포넌트</h1>
    <p>현재 부모의 숫자: {{ myCount }}</p>

    <Child 
      :childTitle="myTitle"
      :count="myCount"
      @increase-count="handleIncrease" 
    />
  </div>
</template>

 

Child.vue

<script setup>
// 1. Props 정의: 부모에게 받을 데이터 이름표를 만듭니다.
const props = defineProps({
  childTitle: String, // 타입 지정 (String, Number 등)
  count: Number
})

// 2. Emits 정의: 부모에게 보낼 신호 이름을 정합니다.
const emit = defineEmits(['increase-count'])

// 버튼 클릭 시 실행할 함수
const sendSignal = () => {
  // 'increase-count'라는 신호를 부모에게 발사! (필요하면 두 번째 인자로 데이터 전달 가능)
  emit('increase-count')
}
</script>

<template>
  <div style="border: 2px solid #42b883; padding: 10px; margin: 10px;">
    <h3>👶 여기는 자식 컴포넌트</h3>
    <p>받은 제목: {{ childTitle }}</p>
    <p>받은 숫자: {{ count }}</p>
    
    <button @click="sendSignal">숫자 올려달라고 조르기 (Emit)</button>
  </div>
</template>