v-if, v-else
디렉티브를 이용하여 조건부 렌더링을 할 수 있다. v-else-if
또한 가능하다.
<div id="app">
<h1 v-if="isShow">hello vue!</h1>
<h1 v-else-if="0">good app!</h1>
<h1 v-else>morning bro</h1>
</div>
<script>
const { createApp } = Vue;
const App = {
data() {
return {
isShow: false,
}
},
}; const vm = createApp(App).mount("#app"); </script>
결과:
아래와 같이 v-else
나 v-else-if
가 바로 전,후 형제요소가 아니라면 작동하지 않는다
<!--- 이렇게 하면 안된다 ---->
<h1 v-if="isShow">hello vue!</h1>
<div>
<h1 v-else-if="0">good app!</h1>
</div>
<h1 v-else>morning bro</h1>
<!--- 이렇게 하세요! ---->
<div v-else-if="true">
<h1>good app!</h1>
<span>우와앙</span>
</div>
위와같이 래핑하고싶지 않을때도 있을 것이다. 그럴땐 template
을 사용하면 그룹화할 수 있음.
<template v-else-if>
<h1>good app!</h1>
<span>우와앙</span>
</template>
참고로 v-for
디렉티브를 사용한 컴포넌트에 절 대 v-if
를 사용하면 안된다
v-if
가 더 높은 우선순위를 가짐(v-for가무시됨)
<!---- 나쁨 ---->
<ul>
<li v-for="" v-if="" :key="">{{data}}</li>
</ul>
<!---- 권장 ---->
<ul>
<template v-for="">
<li v-if="" :key="">{{data}}</li>
</template>
</ul>
v-if
와 비슷하게 동작하지만, 요소 style
태그에 display:none
을 토글한다.
=> v-if
는 DOM에서 제거되지만, v-show
는 남아있다.
전환비용이 높다면 v-show
를 사용하고 아니라면 v-if
를 사용한다.
v-show
를 사용할땐 v-cloak
디렉티브를 같이 사용하면 좋다.
=> v-show
로 인하여display:none
이 부여되기 직전 잠깐동안 보간내용이 보일 수 있음. 따라서 v-cloak
에 스타일로 display:none
을 준다..
참고로 v-cloak
은 연결된 컴포넌트의 인스턴스가 컴파일 완료할때 까지 엘리먼트에 남아있다.
=> 데이터가 바뀌어도 계속 display:none
을 적용하고 있으니 계속 안보이게 할 수 있다.
v-for
을 이용하면 참조형데이터를 순회하며 꺼내올수 있다는걸 안다.
<div id="app">
<h1 v-for="(item, index) in items">{{index}} {{item}}</h1>
</div>
이런식으로 인덱스도 꺼내올 수 있다.
만약 아이템이 객체라면 (value, key, index)
까지 사용할 수 있다. index
는 객체에 존재하지 않지만 임의로 부여해줌.
또한 in
대신 of
를 사용할 수도 있다. 별 차이는 없다.
v-for
를 이용하여 렌더링된 엘리먼트 목록을 갱신할때 key
를 부여해야 최적화가 됨
=> 리액트와 똑같은 내용. 키값을 지정해두면 뷰가 감지하지 못하는 부분도 감지해줄수 있다.
map, filter
등으로 새로운 배열을 할당해도 전부 다 렌더링하지 않는다. 똑똑이 Vue가 최적화를 잘 해주어서 상관없음
<ul v-for="numbers in sets">
<li v-for="n in even(numbers)">{{ n }}</li>
</ul>
<script>
...
data(){
return{
sets: [[1,2,3,4,5], [6,7,8,9,10]]
}
},
methods:{
even(numbers){
return numbers.filter(number => number % 2 === 0)
}
}
</script>
이런식으로 중첩된 내부 노드에 메서드를 할당하여 사용할수도 있음.
Vue에서 컴포넌트를 작성할 수 있다고 했다.
그 컴포넌트 역시 v-for
디렉티브를 사용하여 반복할 수있다.
<div id="app">
<form @submit="addNewTodo">
<label for="new-todo"></label>
<input v-model="newTodoText" id="new-todo" placeholder="플레이스 홀더" />
<button>Add</button>
</form>
<ul>
<todo-item v-for="todo in todos" :key="todo.id" :todo="todo" @remove="removeTodo">
</todo-item>
</ul>
</div>
<script>
const { createApp } = Vue;
const TodoItem = {
template: `
<li>
{{ todo.title }}
<button @click="$emit('remove', todo.id)">Remove</button>
</li>`,
props: ['todo']
}
const generateId = () => `${Date.now()}${Math.random()}`;
const App = {
components: {
TodoItem
},
data() {
return {
newTodoText: '',
todos: []
}
},
methods: {
addNewTodo(e) {
e.preventDefault();
this.todos.push({
id: generateId(),
title: this.newTodoText
});
this.newTodoText = ""
},
removeTodo(todoId) {
this.todos = this.todos.filter(({ id }) => id !== todoId);
}
}
}; const vm = createApp(App).mount("#app"); </script>
$emit
키워드를 사용하면 (eventName, args)
를 넘겨줄수있다. 이때 매개변수는 컴포넌트 내부에서 밖으로 넘겨주는 행위임.
v-on
디렉티브를 사용하면 이벤트 핸들러 등록이 가능하다.
축약표현(@
)을 많이씀
아무런 매개변수를 전달하지 않으면 이벤트를 쉽게 액세스 할수있음
<button @click="handler"></button>
<script>
...
handler(e){
e.target...
}
</script>
매개변수를 전달할때도 이벤트에 액세스하려면 $event
키워드를 인자로 전달해주면 된다.
<button @click="handler(args, $event)"></button>
<script>
...
handler(args, event){
e.target...
}
</script>
preventDefault()
같은 메서드를 작성하지 않아도 되게 만들어줌.
<a @click.prevent="onSubmit">...</a>
다양한 수식어가 존재. 수식어는 체이닝 가능.
보통 스크롤, 휠이벤트를 사용할때 같이 씀.
화면 렌더링과 로직 처리를 분리해준다. 참고로 바닐라JS에서도 사용 가능함.
자세한 내용
나중에 참고해보겠다. 브라우저 렌더링 과정중 합성단계의 일이다.
이벤트 핸들러에 부착된 메서드는 compositer쓰레드에서 처리.
이때 수신한 이벤트를 메인쓰레드에 넘겨준뒤 렌더트리를 받을때 까지 대기하는데, passive를 사용하면 메인쓰레드를 기다리지 않고 페인트작업을 시작함.
<input @keyup.page-dowon="onPageDown"/>
키 이벤트를 케밥-케이스로 수식어를 붙이면, 해당 키가 눌렸을때만 이벤트핸들러 동작함. 와우!
하지만 딱 그 키만 눌렸을때 동작하는 게 아니라 다른 키와 조합해도 동작함.
=> .exact
수식어 사용하면 놀랍게도 딱 그 키만 눌렀을때 동작...!
쉽구먼?
위 컴포넌트의 렌더링 부분 보면 v-model
디렉티브를 사용해서 양방향 바인딩을 했다.
v-model
에 대해 자세히 알아보자.
사실상 문법적 설탕임
<input :input="onInput" value="inputValue"/>
<input v-model="inputValue">
위의 두개는 같은 의미다.
단, v-model
은 초기 value, checked, selected
속성을 무시함.
IME언어는 언어 합성중 v-model
이 업데이트되지 않음
=> 자음모음 합쳐질때 말하는 것. 이때 업데이트가 필요하다면 input
을 사용하라.
checked
는 여러 값을 사용할때 값을 배열로 만들어주면 된다.
input
말고 change
이벤트 이후에 동기화change
는 이벤트 요소 변경이 아예 끝나야 동작.trim()
과 똑같음컴포넌트를 지역, 전역으로 활용할 수 있다.
보통 지역으로 많이 사용하는데, 전역으로 사용하는 방법도 알아놓기만 하자.
<div id="app">
<global-component name="apple"></global-component>
</div>
<script>
const { createApp } = Vue;
const App = {
data() {
return {
}
},
};
const app = createApp(App);
app.component('global-component', {
template: `<div>{{ name }}</div>`,
props: ['name']
});
const vm = app.mount("#app");
</script>
전역 컴포넌트는 케밥-케이스로 작성해야 한다.
props로 받아온 데이터는 readonly
다
따라서 상위에서 처리해주어야함.
<div id="app">
<global-component @to-upper="toUpper(fruit, $event)" v-for="fruit in fruits" :key="fruit.id"
:name="fruit.name"></global-component>
</div>
<script>
const { createApp } = Vue;
const App = {
data() {
return {
fruits: [
{ id: '1', name: 'apple' },
{ id: '2', name: 'banana' },
{ id: '3', name: 'cherry' },
]
}
},
methods: {
toUpper(fruit, upperName) {
this.fruits.find(({ id }) => id === fruit.id).name = upperName
}
}
};
const app = createApp(App);
app.component('global-component', {
template: `<div @click="capitalize">{{ name }}</div>`,
props: ['name'],
methods: {
capitalize() {
this.$emit('to-upper', this.name.toUpperCase());
}
}
});
const vm = app.mount("#app");
</script>
글자를 누르면 어떤 단계로 진행되는지 알아보자.
capitalize
가 실행된다.capitalize
는 컴포넌트 내부에 작성된 메서드 중 하나다.this.$emit
을 이용하여 사용자 정의 이벤트를 송신한다.@to-upper
에 할당된 toUpper()
메서드 인자로 컴포넌트에 할당된 fruit
값과 $event
객체를 통해 capitalize
메서드에서 넘겨준 this.name.toUpperCase()
값을 받아옴.이렇게 나온다.
$emit
은 dispatchEvent
와 비슷해보이지만 다르다고한다.
결론: $emit
을 이용하여 상위컴포넌트로 "이거 좀 처리해주소!"하고 넘겨준다.
수식어가 굉장히 편리해보인다. $emit은 좀 어렵게 느껴졌는데 api
함수를 만들때 사용했던 dispatchEvent
를 떠올리니 쉽게 연상이 됐다.
뷰가 확실히 리액트보단 더 쉬워보인다. 더 최신 기능이라 그렇겠지?
낼도 힘내보자!
안녕하세요 :) 국비지원 부트캠프 엘리스트랙입니다! 오늘도 개발 공부 열심히 하고 계시군요! 멋지십니다 :)
혹시 개발 공부하면서 기술면접에 대한 대비가 막막하시다면, 이번 기술면접 특강도 관심 가져보시면 좋을 것 같아 댓글로 행사 안내드려요~
프론트/ 백엔드 모두 실력있고, 실제 면접관으로 활동하고 계신 개발자 코치님께서 진행하시니 참여해 보세요> https://festa.io/events/4389
그럼 오늘도 화이팅입니다!🙇🏻♀️💪🏻