조건부 렌더링
v-if
, v-else-if
, v-else
디렉티브를 이용해서 조건부 렌더링이 가능하다.
v-if
<h1 v-if="isShow">Hello Vue!</h1>
<template v-else-if="[]">
<h2>Application..</h2>
<p>1234</p>
<span>987</span>
</template>
<h2 v-else>Good Morning</h2>
const App = {
data() {
return {
isShow: null,
};
},
};
const vm = Vue.createApp(App).mount("#app");
특정한 요소에 wrapping 되어서 출력되는 것을 원하지 않을 경우 div
대신 template
태그를 사용할 수 있다.
template
태그를 사용하면 본인은 출력되지 않고 그 안에 있는 요소들만 출력된다.
v-show
v-show
는 요소의 스타일 속성에 display: none
을 추가하여 엘리먼트를 조건부로 표시합니다.
<h1 v-show="isShow">Hello Vue</h1>
다만 주의할 점은 v-show
는 요소를 구조적으로 먼저 생성하고 데이터를 연결하기 때문에 이중 중괄호 구문을 사용하면 데이터로 대체되어 들어가기 전에 해당 중괄호 구문이 화면에 잠깐 출력되는 문제점이 있을 수 있다.
따라서 v-cloak
이라는 디렉티브와 같이 사용하는 것이 좋다.
v-cloak
은 이중 중괄호 구문으로 출력해야 하는 데이터를 사용할 준비가 모두 되었으면 VueJS의 내부 동작을 통해 제거된다.
v-cloack
속성 선택자를 전역으로 추가해서 사용하는 것이 좋다.
[v-clock] {
display: none;
}
<button @click="toggle">Toggle</button>
<h1 v-show="isShow" v-cloak>{{ msg }}</h1>
const App = {
data() {
return {
msg: "Hello Vue!",
isShow: true,
};
},
methods: {
toggle() {
this.isShow = !this.isShow;
},
},
};
const vm = Vue.createApp(App).mount("#app");
v-if
vs v-show
v-if
는 lazy하므로 조건이 true가 될 때까지 렌더링되지 않는다.
따라서 초기 렌더링 비용이 낮은 반면 전환 비용이 높다.
v-show
는 요소의 css 기반으로 전환이 되므로 초기 조건과 관계없이 항상 렌더링 된다.
따라서 초기 렌더링 비용이 높은 반면 전환 비용이 낮다.
리스트 렌더링
v-for
v-for
디렉티브를 사용하여 배열을 순회할 수 있다.
item on items
형태의 문법을 필요로 합니다.
<li v-for="item in items">
{{ item.message }}
</li>
만일 index 값이 필요하다면 (item, index) on items
형태로 적을 수도 있다.
in
대신 of
를 구분 기호로 사용하여 자바스크립트 반복문 문법처럼 사용할 수도 있다.
객체를 순회하는 것도 가능하다.
(value, key) in myObject
의 형태의 문법을 필요로 하며 원한다면 (value, key, index) in myObject
의 형태로 적을 수도 있다.
v-for
는 정수를 사용하여 범위를 순회할 수 있다.
<p v-for="n in 10">{{ n }}</p> // 1 ~ 10 (제로 베이스 인덱스 x)
[ 참고 ]
v-if
와 v-for
를 함께 사용하는 것은 권장되지 않는다.
함께 사용해야 할 필요가 있다면 template
태그를 사용하여 해결할 수 있다.
<template v-for="todo in todos">
<li v-if="!todo.isComplete">
{{ todo.name }}
</li>
</template>
상태 유지
v-for
로 렌더링된 리스트를 업데이트할 때, 리스트 아이템의 순서가 변경된 경우 아이템의 순서와 일치하도록 DOM 엘리먼트를 이동하는 대신, 변경이 필요한 인덱스의 엘리먼트들을 제자리에서 패치해 아이템을 렌더링한다.
이 과정에서 문제가 생기는 것을 방지하기 위해 고유한 key
속성을 제공하는 것이 좋다.
<div v-for="item in items" :key="item.id">
<!-- 내용 -->
</div>
가능한 한 언제나 v-for
는 key
속성과 함께 사용하는 것이 권장된다.
e.g. TodoList
<form @submit.prevent="addNewTodo">
<label for="new-todo">Add a todo</label>
<input
v-model="newTodoText"
id="new-todo"
type="text"
placeholder="E.g. Feed the cat"
/>
<button>Add</button>
</form>
<ul>
<todo-item
v-for="todo in todos"
:key="todo.id"
:todo="todo"
@remove="removeTodo"
/>
</ul>
function generateId() {
return `${Date.now()}${Math.random()}`;
}
const TodoItem = {
template: `
<li>
{{ todo.title}}
<button @click="$emit('remove', todo.id)">Remove</button>
</li>`,
props: ["todo"],
};
const App = {
components: {
TodoItem,
},
data() {
return {
newTodoText: "",
todos: [],
};
},
methods: {
addNewTodo() {
this.todos.push({
id: generateId(),
title: this.newTodoText,
});
this.newTodoText = "";
},
removeTodo(todoId) {
this.todos = this.todos.filter((todo) => {
return todo.id !== todoId;
});
},
},
};
const vm = Vue.createApp(App).mount("#app");
이벤트 핸들링
v-on
디렉티브는 DOM 이벤트를 수신하고 트리거될 때 정의한 자바스크립트 코드를 실행할 수 있다.
인라인 핸들러
이벤트 핸들러의 논리가 간단할 경우 다음과 같이 사용한다.
<button @click="count++">1 추가</button>
인라인 핸들러에서 메소드를 호출할 수도 있다.
사용자 지정 인자와 함께 이벤트 객체를 보내고 싶을 경우 $event
를 사용하여 전달할 수 있다.
<button @click="say('안녕')">안녕이라고 말하기</button>
methods: {
say(message, event) {
// 내용
}
}
메소드 핸들러
이벤트 핸들러의 논리가 복잡할 경우 메소드 이름을 직접 바인딩할 수 있다.
메소드 핸들러는 이벤트 객체를 자동으로 수신한다.
<h1 @click="say">{{ msg }}</h1>
methods: {
say(event) { // 이벤트 객체
console.log(event.target.textContent);
}
}
복합 이벤트 핸들러
여러개의 함수를 실행시키고 싶을 때 인라인 메소드 방식을 사용한다.
하나의 메소드를 실행하고 나서 ,
혹은 ;
로 마무리한다.
<h1 @click="a(); b(); c()">Hello Vue</h1>
<h1 @click="a(), b(), c()">Hello Vue</h1>
이벤트 수식어
v-on
은 점(.)으로 시작하는 지시적 접미사인 이벤트 수식어를 제공한다.
수식어는 순서가 중요하며 체이닝이 가능하다.
<!-- 이벤트 버블링 방지 -->
<a @click.stop="doThis"></a>
<!-- 기본 이벤트 동작 방지 후 작성한 메소드 실행 -->
<form @submit.prevent="onSubmit"></form>
<!-- 이벤트 캡쳐링 -->
<!-- 내부 엘리먼트에서 이벤트 핸들러가 실행되기 전에, 여기에서 먼저 핸들러가 실행 -->
<div @click.capture="doThis">...</div>
<!-- event.target이 엘리먼트 자체인 경우 핸들러 실행-->
<!-- currentTarget과 target이 같은 요소일 경우 -->
<div @click.self="doThat">...</div>
<!-- 이벤트는 단 한 번만 실행 -->
<a @click.once="doThis"></a>
<!-- 로직의 처리와 화면의 동작 독립시킴 -->
<div @scroll.passive="onScroll">...</div>
화면의 구현과 이에 해당하는 로직을 브라우저가 동시에 처리하기 때문에 로직이 복잡하고 양이 많다면 그만큼 부하가 발생한다.
.passive
는 화면의 동작과 로직 처리를 독립시키기 때문에 부하를 줄일 수 있다.
화면은 부드러워보이지만 로직은 매우 열심히 돌아가고 있는 구조가 되는 것이다.
.passive
수식어는 모바일 환경에서 성능 향상에 도움이 된다.
.passive
는 브라우저에게 이벤트의 기본 동작을 방지하지 않겠다는 의도를 전달한 것이다.
따라서 .passive
와 .prevent
를 함께 사용해서는 안된다.
입력키 수식어
<!-- key가 Enter일 때만 submit 호출 -->
<input @keyup.enter="submit" />
.exact
수식어를 사용하면 이벤트를 트리거하는 데 필요한 시스템 수식어의 정확한 조합을 제어할 수 있다.
<!-- Ctrl과 함께 Alt 또는 Shift를 누른 상태에서도 클릭하면 실행 -->
<button @click.ctrl="onClick">A</button>
<!-- 오직 Ctrl만 누른 상태에서 클릭해야 실행 -->
<button @click.ctrl.exact="onCtrlClick">A</button>
폼 입력 바인딩
v-model
디렉티브를 사용하면 양방향 데이터 바인딩이 가능하다.
v-model은 :value
와 @input
을 단축시킨 것과 동일하다.
// :value="message"
// @input="message = $event.target.value"
<p>메세지: {{ message }}</p>
<input v-model="message" placeholder="메세지 입력하기" />
유의해야 할 점은 v-model
은 한글을 작성할 때 영어와 달리 데이터가 즉시 바인딩이 되지 않고 글자가 완성이 되어야만 바인딩이 된다.
따라서 한글을 처리할 때는 v-model
을 사용하지 않고 다음과 같이 작성하여 문제점을 해결할 수 있다.
<input :value="msg" @input="msg = $event.target.value" />
체크박스
배열에 체크박스 값을 바인딩 할 수 있다.
<input type="checkbox" v-model="checked" value="1번" />
<input type="checkbox" v-model="checked" value="2번" />
<input type="checkbox" v-model="checked" value="3번" />
data() {
return {
checked: [], // {0: '1번', 1: '2번', 2: '3번'}
};
},
배열 데이터를 연결할 경우, 체크 여부에 따라 value의 값이 객체 형태로 담기게 된다.
셀렉트
v-model
을 사용하여 선택한 옵션의 value를 데이터에 담을 수 있다.
<select v-model="selected">
<option value="">과일을 선택해주세요.</option>
<option v-for="fruit in fruits"
:value="fruit.value"
:key="fruit.value">{{ fruit.text }}</option>
</select>
data() {
return {
selected: "",
fruits: [
{ value: "사과", text: "apple" },
{ value: "오렌지", text: "orange" },
{ value: "바나나", text: "banana" },
],
};
},
수식어
.lazy
: input 이벤트로 동 작하는 양방향 데이터 바인딩을 change로 변환
<input v-model.lazy="msg" />
.number
: 사용자 입력을 자동으로 숫자로 형변환
<input v-model.number="msg" />
.trim
: 사용자가 입력한 내용에서 자동으로 앞뒤 공백 제거
<input v-model.trim="msg" />
컴포넌트 기초
컴포넌트를 사용하면 UI를 독립적이고 재사용 가능한 일부분으로 분할하고 각 부분을 개별적으로 다룰 수 있습니다.
전역 등록
컴포넌트의 등록 방법은 전역 등록과 지역 등록으로 나뉜다.
app.component()
메서드를 사용하여 현재 뷰 어플리케이션에서 컴포넌트를 전역으로 사용할 수 있도록 할 수 있다.
const app = createApp({})
app.component(
'MyComponent',
// 구현체
{
/* ... */
}
)
하위 컴포넌트의 이벤트 수신
props는 외부에서 들어오는 데이터이기 때문에 readonly
이다.
그래서 데이터를 수정하려면 데이터가 들어오는 외부에서 변경을 해주어야 한다.
<upper-name
v-for="fruit in fruits"
:name="fruit.name"
:key="fruit.id"
@to-upper="toUpper(fruit, $event)"
></upper-name>
<!-- 커스텀 이벤트에서 $event 객체는 $emit에서 두 번째 인자로 올려준 데이터 -->
const App = {
data() {
return {
fruits: [
{ id: 1, name: "apple" },
{ id: 2, name: "orange" },
{ id: 3, name: "banana" },
],
};
},
methods: {
toUpper(fruit, upperName) {
fruit.name = upperName;
},
},
};
const app = Vue.createApp(App);
app.component("upper-name", {
props: ["name"],
template: `<div @click="capitalize">{{ name }}</div>`,
methods: {
capitalize() {
this.$emit("to-upper", this.name.toUpperCase());
// 데이터를 컴포넌트 바깥으로 올림
},
},
});
const vm = app.mount("#app");
하위 컴포넌트에서는 상위에서 props로 내려준 데이터를 수정할 수 없다.
따라서 하위 컴포넌트 내부에서 emit 커스텀 이벤트를 통해 상위에 전달하고 상위에서 이벤트를 받으면 작성해놓은 이벤트를 실행시켜서 데이터를 변경한다.
출처: 프로그래머스 프론트엔드 데브코스
[Day 35] Vue (3)
'데브코스' 카테고리의 다른 글
[Day 36-2] 컴포넌트 등록, props, 커스텀 이벤트 (0) | 2022.12.09 |
---|---|
[Day 36-1] Node.js, npm, Parcel, Webpack (0) | 2022.12.07 |
[Day 34] computed, watch, 데이터 바인딩 (0) | 2022.12.05 |
[Day 33] 반응성, 디렉티브, 라이프 사이클, data & methods (0) | 2022.12.02 |
[Day 32] SCSS (2) (0) | 2022.11.30 |