🔷 DOM을 기본 구성 요소 인스턴스 데이터에 선언적으로 바인딩할 수 있는 HTML 기반 템플릿 구문을 사용
1. Text Interpolation
<p>Message: {{ msg }}</p>
2. Raw HTML
<div v-html="rawHtml"></div>
const rawHtml = ref('<span style="color:red">This should be red.</span>')
❗ 웹사이트에서 임의의 HTML을 동적으로 렌더링하면 XSS 취약점이 쉽게 발생할 수 있다. 이는 매우 위험하기 때문에 사용자가 제공한 컨텐츠에서의 사용은 금지한다.
3. Attribute Bindings
<div v-bind:id="dynamicId"></div>
const dynamicId = ref('my-id')
4. JavaScript Expressions
{{ number + 1 }}
{{ ok ? 'YES' : 'NO’ }}
{{ message.split('').reverse().join('') }}
<div :id="`list-${id}`"></div>
❗표현식 주의사항❗
각 바인딩에는 하나의 단일 표현식만 포함될 수 있다.
표현식이 아닌 선언식이나 if 같은 흐름제어도 작동하지 않는다.
🔷 v-
접두사가 있는 특수 속성
<p v-if="seen">Hi There</p>
요소를 제거 / 삽입
🔷 Directive 전체 구문
🔷 Arguments
<a v-bind:href="myUrl">Link</a>
<button v-on:click="doSomething">Button</button>
🔷 Modifiers
event.preventDefault()
를 호출하도록<form @submit.prevent="onSubmit">...</form>
🔷 Built-in Directives
(참고 링크) Vue 공식 페이지 중
🖥 template-syntax.html
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<title>Vue</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
<div id="app">
<!-- 기본 구문 콧수염 방식으로 꺼내오기 -->
<p>{{msg}}</p>
<!-- html 요소 v-html로 가져오기 -->
<p v-html="rawHtml"></p>
<!-- 동적 Id 부여 -->
<div v-bind:id="dynamicId">동적 ID</div>
<!-- JS expressions -->
<p>{{number+1}}</p>
<p>{{ok ? 'Yes' : 'No'}}</p>
<div :id="`list-${number}`">동적</div>
<div v-if="ok">보임?</div>
</div>
<script>
const { createApp, ref } = Vue;
const app = createApp({
setup() {
const msg = ref("hello vue");
const rawHtml = ref('<span style="color:red">This is a red.</span>');
const dynamicId = ref("my-id");
const number = ref(0);
const ok = ref(false);
return {
msg,
rawHtml,
dynamicId,
number,
ok,
};
},
});
app.mount("#app");
</script>
</body>
</html>
setup을 통해 ok를 true로 설정하면 변화한다.
🔷하나 이상의 속성 또는 컴포넌트 데이터를 표현식에 동적으로 바인딩
1. Attribute Bindings
<!-- v-bind.html -->
<img v-bind:src="imageSrc">
<a v-bind:href="myUrl">Move to url</a>
: (colon)
<img :src="imageSrc">
<a :href="myUrl">Move to url</a>
<button :[key]="myValue"></button>
🖥 V-bind.html
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<title>Vue</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
<div id="app">
<img :src="imageSrc" />
<a :href="myUrl">MyUrl 이동</a>
<p :[dynamicattr]="dynamicValue">...</p>
</div>
<script>
const { createApp, ref } = Vue;
const app = createApp({
setup() {
const imageSrc = ref("https://picsum.photos/200");
const myUrl = ref("https://www.google.com");
const dynamicattr = ref('title')
const dynamicValue = ref('Hello Vue.js')
return {
imageSrc,
myUrl,
dynamicattr,
dynamicValue,
};
},
});
app.mount("#app");
</script>
</body>
</html>
2. Class and Style Bindings
🔷 클래스와 스타일은 모두 속성이므로 v-bind를 사용하여 다른 속성과 마찬가지로 동적으로 문자열 값을 할당할 수 있음
1) Binding HTML Classes
🔷 Binding to Objects: 객체를 :class
에 전달하여 클래스를 동적으로 전환할 수 있음
예시) isActive 의 T/F에 의해 active 클래스의 존재가 결정됨
const isActive = ref(false)
<div :class="{ active: isActive }">Text</div>
예시) :class directive
를 일반 클래스 속성과 함께 사용 가능
const isActive = ref(false)
const hasInfo = ref(true)
<div class="static" :class="{ active: isActive, 'text-primary': hasInfo }">Text</div>
const isActive = ref(false)
const hasInfo = ref(true)
// ref는 반응 객체의 속성으로 액세스되거나 변경될 때 자동으로 unwrap
const classObj = ref({
active: isActive,
'text-primary': hasInfo
})
<div class="static" :class="classObj">Text</div>
🔷 Binding to Arrays
:class
를 배열에 바인딩하여 클래스 목록을 적용할 수 있음const activeClass = ref('active')
const infoClass = ref('text-primary')
<div :class="[activeClass, infoClass]">Text</div>
<div :class="[{ active: isActive }, infoClass]">Text</div>
🖥 binding-html-classes.html
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<title>Vue</title>
<style>
.active {
color: crimson;
}
.text-primary {
color: blue;
}
</style>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
<div id="app">
<div :class="{active: isActive}">글자색</div>
<div class="static" :class="{active: isActive, 'text-primary': hasInfo}">글자색</div>
<div :class="classObj">글자색</div>
<div :class="[activeClass, infoClass]">글자색</div>
<div :class="[{active: isActive}, infoClass]">글자색</div>
</div>
<script>
const { createApp, ref } = Vue;
const app = createApp({
setup() {
const isActive = ref(false);
const hasInfo = ref(true);
//ref는 반응 객체의 속성으로 액세스되거나 변경될 때 자동으로 unwrap
const classObj = ref({
active: isActive,
'text-primary': hasInfo,
});
const activeClass = ref('active');
const infoClass = ref('text-primary');
return {
isActive,
hasInfo,
classObj,
activeClass,
infoClass,
};
},
});
app.mount("#app");
</script>
</body>
</html>
isActive와 hasInfo의 true, false에 따라 이리저리 바뀐다.
2. Binding Inline Styles
🔷 Binding to Objects
:style
은 JavaScript 객체 값에 대한 바인딩을 지원 (HTML style 속성에 해당)const activeColor = ref('crimson')
const fontSize = ref(50)
<div :style="{ color: activeColor, fontSize: fontSize + 'px' }">Text</div>
:style
은 kebab-cased 키 문자열도 지원 (단, camelCase 작성 권장)<div :style="{ 'font-size': fontSize + 'px' }">Text</div>
const styleObj = ref({
color: activeColor,
fontSize: fontSize.value + 'px'
})
<div :style="styleObj">Text</div>
🔷 Binding to Arrays
:style
을 바인딩할 수 있음const styleObj2 = ref({
color: 'blue',
border: '1px solid black'
})
<div :style="[styleObj, styleObj2]">Text</div>
🖥 binding-inline-styles.html
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<title>Vue</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
<div id="app">
<div :style="{color: activeColor, fontSize: fontSize+'px'}">글자</div>
<!-- kebap 케이스로도 가능하다 (하지만 카멜 케이스 권장) -->
<div :style="{'font-size': fontSize+'px'}">글자</div>
<div :style="styleObj">글자</div>
<div :style="[styleObj, styleObj2]">글자</div>
</div>
<script>
const { createApp, ref } = Vue;
const app = createApp({
setup() {
const activeColor = ref("crimson");
const fontSize = ref(50);
const styleObj = ref({
color: activeColor,
fontSize: fontSize.value + "px",
});
const styleObj2 = ref({
color: "blue",
border: "1px solid black",
});
return {
activeColor,
fontSize,
styleObj,
styleObj2,
};
},
});
app.mount("#app");
</script>
</body>
</html>
🔷 v-on
: DOM 요소에 이벤트 리스너를 연결 및 수신
🔷 v-on shorthand (약어) → @
v-on:event="handler"
//or
@event="handler"
🔷 handler 종류
1. Inline handlers : 이벤트가 트리거 될 때 실행 될 JavaScript 코드
const count = ref(0)
<button @click="count++">Add</button>
<p>Count: {{ count }}</p>
2. Method handlers : 컴포넌트에 정의된 메서드 이름
const name = ref('Alice')
const myFunc = function (event) {
console.log(event)
console.log(event.target)
console.log(`Hello ${name.value}!`)
}
<button @click="myFunc">Hello</button>
const greeting = function (message) {
console.log(message)
}
<button @click="greeting('hello')">Say hello</button>
<button @click="greeting('bye')">Say bye</button>
🔷 Inline Handlers에서의 event 인자에 접근하기
const warning = function (message, event) {
console.log(message)
console.log(event)
}
<button @click="warning('경고입니다.', $event)">Submit</button>
🔷 Event Modifiers
event.preventDefault()
와 같은 구문을 메서드에서 작성하지 않도록 함<form @submit.prevent="onSubmit">...</form>
<a @click.stop.prevent="onLink">...</a>
❗ Modifiers는 chained 되게끔 작성할 수 있으며 이때는 작성된 순서로 실행되기 때문에 작성 순서에 유의한다.
🔷 Key Modifiers
<input @keyup.enter="onSubmit">
🖥 event-handling.html
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<title>Vue</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
<div id="app">
<!-- inline -->
<p>Count: {{count}}</p>
<button @click="count++">증가</button>
<button @click="count--">감소</button>
<!-- 메서드 -->
<button @click="myFunc">메서드 동작</button>
<button @click="greeting('해윙~')">해윙~</button>
<button @click="warning('보', $event)">보</button>
<a @click.stop.prevent="onLink" href="https://www.google.com">링크</a>
<input type="text" @keyup.enter="onSubmit" />
</div>
<script>
const { createApp, ref } = Vue;
const app = createApp({
setup() {
const count = ref(0);
const name = ref("Alice");
const myFunc = (event) => {
alert(event);
console.log(event.target);
console.log(`Hello ${name.value}`);
};
const greeting = (message) => {
console.log(message)
};
const warning = (message, event) => {
console.log(message);
console.log(event);
};
const onLink = () => {
console.log("link click")
};
const onSubmit = () => {
console.log("onSubmit");
};
return {
count,
name,
myFunc,
greeting,
warning,
onLink,
onSubmit,
};
},
});
app.mount("#app");
</script>
</body>
</html>
다양한 메서드를 동작시켜볼 수 있다.
🔷 form을 처리할 때 사용자가 input에 입력하는 값을 실시간으로 JavaScript 상태에 동기화해야 하는 경우, 양방향 바인딩을 필요로 한다.
🔷 양방향 바인딩 방법
1. v-bind
와 v-on
을 함께 사용
v-bind
를 사용하여 input 요소의 value 속성 값을 입력 값으로 사용v-on
을 사용하여 input 이벤트가 발생 할 때마다 input 요소의 value 값을const inputText1 = ref(‘’)
const onInput = function (event) {
inputText1.value = event.currentTarget.value
}
<p>{{ inputText1 }}</p>
<input :value="inputText1" @input="onInput">
2. v-model
사용
v-model
: form input 요소 또는 컴포넌트에서 양방향 바인딩을 만듦v-model
을 사용하여 사용자 입력 데이터와 반응형 변수를 실시간 동기화const inputText2 = ref('')
<p>{{ inputText2 }}</p>
<input v-model="inputText2">
💡 IME (Input Method Editor)
사용자가 입력 장치에서 기본적으로 사용할 수 없는 문자(비영어권 언어)를 입력할 수 있도록 하는 운영체제 구성 프로그램으로 일반적으로 키보드 키보다 자모가 더 많은 언어에서 사용해야 한다. IME가 동작하는 방식과 Vue의 V-model 동작 방식이 상충하여 한국어 입력 시 예상대로 동작하지 않게 된다.
🖥 form-input-bindings.html
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<title>Vue</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
<div id="app">
<input type="text" @input="onInput"/>
<p>{{inputText1}}</p>
<hr>
<input type="text" v-model="inputText2"/>
<p>{{inputText2}}</p>
</div>
<script>
const { createApp, ref } = Vue;
const app = createApp({
setup() {
const inputText1 = ref("");
const inputText2 = ref("");
const onInput = () => {
inputText1.value = event.currentTarget.value;
};
return {
inputText1,
inputText2,
onInput,
}
},
});
app.mount("#app");
</script>
</body>
</html>
실시간으로 input 안의 텍스트에 맞춰 output이 변화한다.
🔷 v-model과 다양한 입력(input) 양식
🖥 v-model.html
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<title>Vue</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
<div id="app">
<input type="checkbox" v-model="checked" id="check"/>
<label for="check">{{checked}}</label>
<hr>
<div>Checkednames : {{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>
<input type="checkbox" id="Bzeromo" value="Bzeromo" v-model="checkedNames" />
<label for="Bzeromo">Bzeromo</label>
<hr>
<select v-model="selected" multiple>
<option value="" disabled>선택 해보소</option>
<option>Alice</option>
<option>Bella</option>
<option>Bzeromo</option>
<option>Kevin</option>
</select>
<div>{{selected}}</div>
</div>
<script>
const { createApp, ref } = Vue;
const app = createApp({
setup() {
const checked = ref(false);
const checkedNames = ref([]);
const selected = ref("");
return {
checked,
checkedNames,
selected,
};
},
});
app.mount("#app");
</script>
</body>
</html>
리액트보다 이해하기 쉬운건 기분탓?
아니면 리액트와 비슷하기 때문에 이해하기 쉬운 것?