
DOM을 기본 구성 요소 인스턴스의 데이터에
선언적으로 바인딩할 수 있는 HTML 기반 템플릿 구문을 사용
<p>Message: {{ msg }}</p>
<div v-html="rawHtml"></div>
<script>
const rawHtml = ref('<span style="color:red">This should be red.</span>')
</script>
<div v-bind:id="dynamicId"></div>
<script>
const dynamicId = ref('my-Id')
</script>
{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split('').reverse().join('') }}
<div :id="`list-${id}`"></div>
각 바인딩에는 하나의 단일 표현식만 포함될 수 있음
- 표현식은 값으로 평가할 수 있는 코드 조각 (return 뒤에 사용할 수 있는 코드여야 함)
작동하지 않는 경우
<!-- 표현식이 나닌 선언식 -->
{{ const number = 1 }}
<!-- 제어문은 삼항 표현식을 사용해야 함 -->
{{ if (ok) { return message } }}
'v-' 접두사가 있는 특수 속성
Directive의 속성 값은 단일 JavaScript 표현식이어야 함
(v-for, v-on 제외)
표현식 값이 변경될 때 DOM에 반응적으로 업데이트 적용
예시
<p v-if="seen">Hi There</p>

일부 directive는 directive 뒤에 콜론(":")으로 표시되는 인자를 사용할 수 있음
아래 예시의 href는 HTML <a> 요소의 href 속성 값을 myUrl 값에 바인딩 하도록 하는 v-bind의 인자
<a v-bind:href="myUrl">Link</a>
아래 예시의 click은 이벤트 수신할 이벤트 이름을 작성하는 v-on의 인자
button v-on:click="doSomething">Button</button>
<form @submit.prevent="onSubmit">...</form>하나 이상의 속성 또는 컴포넌트 데이터를
표현식에 동적으로 바인딩
<img v-bind:src="imageSrc" alt="imageSrc">
<a v-bind:href="myUrl">Move to url</a>
<img :src="imageSrc" alt="imageSrc">
<a :href="myUrl">Move to url</a>
<button :[key]="myValue"></button><img :src="imageSrc" alt="#">
<a :href="myUrl">이동!!</a>
<p :[dynamicattr]="dynamicValue">......</p>
<script>
const { createApp, ref } = Vue
const app = createApp({
setup() {
const imageSrc = ref('https://picsum.photos/200')
const myUrl = ref('https://www.google.co.kr/')
const dynamicattr = ref('title')
const dynamicValue = ref('Hello Vue.js')
return {
imageSrc,
myUrl,
dynamicattr,
dynamicValue
}
}
})
app.mount('#app')
</script>

객체를 :class에 전달하여 클래스를 동적으로 전환할 수 있음
예시 1
- isActive의 Boolean 값에 의해 active 클래스의 존재가 결정됨
<script>
const isActive = ref(false)
</script>
<div :class="{ active: isActive }">Text</div>
객체에 더 많은 필드를 포함하여 여러 클래스를 전환할 수 있음
예시 2
- :class directive를 일반 클래스 속성과 함께 사용 가능
<script>
const isActive = ref(false)
const hasInfo = ref(true)
</script>
<div class="static" :class="{ active: isActive, 'text-primary': hasInfo }">Text</div>
<script>
const isActive = ref(false)
const hasInfo = ref(true)
// ref는 반응 객체의 속성으로 엑서스되거나 변경될 때 자동으로 unwrap
class classObj = ref({
active: isActive,
'text-primary': hasInfo
})
</script>
<div class="static" :class="classObj">Text</div>
:class를 배열에 바인딩하여 클래스 목록을 적용할 수 있음
예시 1
<script>
const activeClass = ref('active')
const infoClass = ref('text-primary')
</script>
<div :class="[activeClass, infoClass]">Text</div>
배열 구문 내에서 객체 구문을 사용하는 경우
예시 2
<div :class="[{active: isActive}, infoClass]">Text</div>
:style은 JavaScript 객체 값에 대한 바인딩을 지원
(HTML style 속성에 해당)
예시 1
<script>
const activeColor = ref('crimson')
const fontSize = ref(50)
</script>
<div :style="{ color: activeColor, fontSize: fontSize + 'px'}">Text</div>
실제 CSS에서 사용하는 것처럼 :style은 kebab-cased 키 문자열도 지원
(단, camelCase 작성을 권장)
예시 2
<div :style="{ color: activeColor, 'font-size': fontSize + 'px'}">Text</div>
반드시 inline 방식으로 작성하지 않아도 됨
반응형 변수를 활용해 객체를 한번에 작성하는 방법
예시 3
<script>
const styleObj = ref({
color: activeColor,
fontSize: fontSize.value + 'px'
})
</script>
<div :style="styleObj">Text</div>
여러 스타일 객체를 배열에 작성해서 :style을 바인딩할 수 있음
작성한 객체는 병합되어 동일한 요소에 적용
예시 3
<script>
const styleObj2 = ref({
color: 'blue',
border: '1px solid black'
})
</script>
<div :style="[styleObj, styleObj2]">Text</div>
DOM 요소에 이벤트 리스너를 연결 및 수신
v-on:event="handler"
@event="handler"<script>
const count = ref(0)
</script>
<button @click="count++">Add 1</button>
<p>Count: {{ count }}</p>
<script>
const myFunc = function (event) {
console.log(event)
console.log(event.currentTarget)
console.log(`Hello, ${name.value}`)
}
</script>
<button @click="myFunc">Hello</button>
<script>
const greeting = function (message) {
console.log(message)
}
</script>
<button @click="greeting('hello')">Say hello</button>
<button @click="greeting('bye')">Say bye</button>
<script>
const warning = function (message, event) {
console.log(message)
console.log(event)
}
</script>
<button @click="warning('경고입니다.', $event)">Submit</button>
<script>
const warning = function (message, event) {
console.log(message)
console.log(event)
}
</script>
<form @submit.prevent="onSubmit">...</form>
<a @click.stop.prevent="onLink">...</a>
Vue는 키보드 이벤트를 수신할 때 특정 키에 관한 별도 modifiers를 사용할 수 있음
예시
- key가 Enter 일 때만 onSubmit 이벤트를 호출하기
<input @keyup.enter="onSubmit">
form을 처리할 때 사용자가 input에 입력하는 값을
실시간으로 JavaScript 상태에 동기화해야하는 경우 (양방향 바인딩)
양방향 바인딩 방법
1. v-bind와 v-on을 함께 사용
2. v-model 사용

<script>
const inputText1 = ref('')
const onInput = function (event) {
inputText1.value = event.currentTarget.value
}
</script>
<p>{{ inputText1 }}</p>
<input :value="inputText1" @input="onInput">
form input 요소 또는 컴포넌트에서 양방향 바인딩을 만듦
<script>
const inputText2 = ref('')
</script>
<p>{{ inputText2 }}</p>
<input v-model="inputText2">

<script>
const checked = ref(false)
</script>
<input type="checkbox" id="checkbox" v-model="checked">
<label for="checkbox">{{ checked }}</label>

<script>
const checkedNames = ref([])
</script>
<div>Checked names: {{ checkedNames }}</div>
<input type="checkbox" id="alice" value="Alice" v-model="checkedNames">
<label for="alice">Alice</label>
<input type="checkbox" id="bella" value="Bella" v-model="checkedNames">
<label for="bella">Bella</label>

<script>
const selected = ref('')
</script>
<select v-model="selected">
<option disabled value="">Please select one</option>
<option>Alice</option>
<option>Bella</option>
<option>Cathy</option>
</select>
"계산된 속성"을 정의하는 함수
-> 미리 계산된 속성을 사용하여 템플릿에서 표현식을 단순하게 하고 불필요한 연산을 줄임
<script>
const todos = ref([
{ text: 'Vue 실습' },
{ text: '자격증 공부' },
{ text: 'TIL 작성' }
])
</script>
<h2>남은 할 일</h2>
<p>{{ todos.length > 0 ? '아직 남았다' : '퇴근!' }}</p>
-> 템플릿이 복잡해지며 todos에 따라 계산을 진행하게 됨
-> 만약 이 계산을 템플릿에 여러 번 사용하는 경우에는 반복이 발생
<script>
const { createApp, ref, computed } = Vue
const restOfTodos = computed(() => {
return todos.value.length > 0 ? '아직 남았다' : '퇴근!'
})
</script>
<h2>남은 할 일</h2>
<p>{{ restOfTodos }}</p>
computed 특징
<script>
const restOfTodos = computed(() => {
return todos.value.length > 0 ? '아직 남았다' : '퇴근!'
})
</script>
<script>
const restOfTodos = function(() => {
return todos.value.length > 0 ? '아직 남았다' : '퇴근!'
})
</script>
<p>{{ getRestOfTodos() }}</p>
-> 반면, method 호출은 다시 렌더링이 발생할 때마다 항상 함수를 실행
-> 무조건 computed만 사용하는 것이 아니라 사용 목적과 상황에 맞게 computed와 method를 적절히 조합하여 사용
표현식 값의 true/false를 기반으로 요소를 조건부로 렌더링

<script>
const isSeen = ref(true)
</script>
<p v-if="isSeen">true일때 보여요</P>
<p v-else>false일때 보여요</p>
<button @click="isSeen = !isSeen">토글</button>
<script>
const name = ref('Cathy')
</script>
<div v-if="name === 'Alice'">Alice입니다</div>
<div v-else-if="name === 'Bella'">Bella입니다</div>
<div v-else-if="name === 'Cathy'">Cathy입니다</div>
<div v-else>아무도 아닙니다.</div>
<template v-if="name === 'Cathy'">
<div>Cathy입니다</div>
<div>나이는 30살입니다</div>
</template>
-> "보이지 않는 wrapper 역할"
표현식 값의 true/false를 기반으로 요소의 가시성을 전환
<script>
const isShow = ref(false)
</script>
<div v-show="isShow">v-show</div>

-> 콘텐츠를 매우 자주 전환해야 하는 경우에는 v-show를, 실행 중에 조건이 변경되지 않는 경우에는 v-if를 권장
소스 데이터를 기반으로 요소 또는 템플릿 블록을 여러 번 렌더링
<div v-for="item in items">
<!-- content -->
<p>{{ item.text }}</p>
</div>
<div v-for="(item, index) in arr"></div>
<div v-for="value in object"></div>
<div v-for="(value, key) in object"></div>
<div v-for="(value, key, index) in object"></div>
<script>
const myArr = ref([
{ name: 'Alice', age: 20 },
{ name: 'Bella', age: 21 }
])
</script>
<div v-for="(item, index) in myArr">
{{ index }} / {{ item }}
</div>
<script>
const myObj = ref({
name: 'Cathy',
age: 30
})
</script>
<div v-for="(value, key, index) in myObj">
{{ index }} / {{ key }} / {{ value }}
</div>
<ul>
<template v-for="item in myArr">
<li>{{ item.name }}</li>
<li>{{ item.age }}</li>
<hr>
</template>
</ul>
<script>
const myInfo = ref([
{ name: 'Alice', age: 20, friends: ['Bella', 'Cathy', 'Dan'] },
{ name: 'Bella', age: 21, friends: ['Alice', 'Cathy'] }
])
</script>
<ul v-for="info in myInfo">
<li v-for="friend in info.friends">
{{ info.name }}의 친구들 - {{ friend }}
</li>
</ul>
내부 컴포넌트의 상태를 일관 되게 하여
데이터의 예측 가능한 행동들을 유지하기 위함
<script>
let id = 0
const items = ref([
{ id: id++, name: 'Alice' },
{ id: id++, name: 'Bella' },
])
</script>
<div v-for="item in items" :key="item.id">
<!-- content -->
<p>{{ item }}</p>
</div>
-> Vue 내부 동작 관련된 부분이기에 최대한 작성하려고 노력할 것
동일한 요소에서 v-if가 v-for보다 우선순위가 더 높기 때문
-> v-if에서의 조건은 v-for범위의 변수에 접근할 수 없음
<script>
const todos = ref([
{ id: id++, name: '복습', isComplete: true },
{ id: id++, name: '예습', isComplete: false },
{ id: id++, name: '저녁식사', isComplete: true },
{ id: id++, name: '노래방', isComplete: false }
])
</script>
<ul>
<li v-for="todo in completeTodos" :key="todo.id">
{{ todo.name }}
</li>
</ul>
<script>
const completeTodos = computed(() => {
return todos.value.filter((todo) => !todo.isComplete)
})
</script>
<ul>
<li v-for="todo in completeTodos" :key="todo.id">
{{ todo.name }}
</li>
</ul>
<ul>
<template v-for="todo in todos" :key="todo.id">
<li v-if="todo.isComplete === false">
{{ todo.name }}
</li>
</template>
</ul>
하나 이상의 반응형 데이터를 감시하고,
감시하는 데이터가 변경되면 콜백 함수를 호출
<script>
watch(count, (newValue, oldValue) => {
console.log(`변화된 값: ${newValue} / 변하기 전 값: ${oldValue}`)
})
</script>
<button @click="count++">Add 1</button>
<p>Count: {{ count }}</p>
<script>
const count = ref(0)
watch(count, (newValue, oldValue) => {
console.log(`변화된 값: ${newValue} / 변하기 전 값: ${oldValue}`)
})
</script>

<input v-model="message">
<p>Message length: {{ messageLength }}</p>
<script>
const message = ref('')
const messageLength = ref(0)
watch(message, (newValue) => {
messageLength.value = newValue.length
})
</script>

<script>
watch([foo, bar], ([newFoo, newBar], [prevFoo, prevBar]) => {
/* ... */
})
</script>
Computed Watchers
공통점 데이터의 변화를 감지하고 처리
동작 의존하는 데이터 속성의 계산된 값을 반환 "특정 데이터 속성의 변화를
감지하고 작업을 수행 (side-effects)"
사용 목적 "계산한 값을 캐싱하여 재사용
중복 계산 방지" 데이터 변화에 따른 특정 작업을 수행
사용 예시 연산 된 길이, 필터링 된 목록 계산 등 "DOM 변경,
다른 비동기 작업 수행, 외부 API와 연동 등"
Computed Watchers
공통점 데이터의 변화를 감지하고 처리
동작 의존하는 데이터 속성의 계산된 값을 반환 "특정 데이터 속성의 변화를
감지하고 작업을 수행 (side-effects)"
사용 목적 "계산한 값을 캐싱하여 재사용
중복 계산 방지" 데이터 변화에 따른 특정 작업을 수행
사용 예시 연산 된 길이, 필터링 된 목록 계산 등 "DOM 변경,
다른 비동기 작업 수행, 외부 API와 연동 등"
※ computed와 watch 모두 의존(감시)하는 원본 데이터를 직접 변경하지 않음
Computed Watchers
공통점 데이터의 변화를 감지하고 처리
동작 의존하는 데이터 속성의 계산된 값을 반환 "특정 데이터 속성의 변화를
감지하고 작업을 수행 (side-effects)"
사용 목적 "계산한 값을 캐싱하여 재사용
중복 계산 방지" 데이터 변화에 따른 특정 작업을 수행
사용 예시 연산 된 길이, 필터링 된 목록 계산 등 "DOM 변경,
다른 비동기 작업 수행, 외부 API와 연동 등"
※ computed와 watch 모두 의존(감시)하는 원본 데이터를 직접 변경하지 않음
Computed Watchers
공통점 데이터의 변화를 감지하고 처리
동작 의존하는 데이터 속성의 계산된 값을 반환 "특정 데이터 속성의 변화를
감지하고 작업을 수행 (side-effects)"
사용 목적 "계산한 값을 캐싱하여 재사용
중복 계산 방지" 데이터 변화에 따른 특정 작업을 수행
사용 예시 연산 된 길이, 필터링 된 목록 계산 등 "DOM 변경,
다른 비동기 작업 수행, 외부 API와 연동 등"
※ computed와 watch 모두 의존(감시)하는 원본 데이터를 직접 변경하지 않음

Vue 인스턴스의 생애주기 동안 특정 시점에 실행되는 함수

const { createApp, ref, onMounted } = Vue
setup() {
onMounted(() => {
console.log('mounted')
})
}

<button @click="count++">Add 1</button>
<p>Count: {{ count }}</p>
<p>{{ message }}</p>
<script>
const { createApp, ref, onMounted, onUpdated } = Vue
const count = ref(0)
const message = ref(null)
onUpdated(() => {
message.value = 'updated!'
})
</script>



<script>
const numbers = ref([1, 2, 3, 4, 5])
const evenNumbers = computed(() => {
return numbers.value.filter((number) => number % 2 === 0)
})
</script>
<li v-for="num in evenNumbers">
{{ num }}
</li>
<script>
const numberSets = ref([
[1, 2, 3, 4, 5],
[6, 7, 8, 9, 10]
])
const evenNumberSets = function (numbers) {
return numbers.filter((number) => number % 2 === 0)
}
</script>
<ul v-for="numbers in numberSets">
<li v-for="num in evenNumberSets(numbers)">
{{ num }}
</li>
</ul>
<div v-for="(item, index) in items" :key="index">
<!-- content -->
</div>
-> 직접 고유한 값을 만들어내는 메서드를 만들거나 외부 라이브러리 등을 활용하는 등 식별자 역할을 할 수 있는 값을 만들어 사용