728x90
★ 컴포넌트 분할과 정의
- 재사용성, 테스트 용이성, 디버깅 편의성 등을 고려해 컴포넌트를 분할하는 것을 권장하고 있다.
- 한 번에 변경되는 데이터를 렌더링하는 UI 단위로 컴포넌트를 분할하는 편이 좋다.
- 데이터가 변경되는 단위로 컴포넌트를 세분화하면 해당 컴포넌트만 다시 렌더링하고 나머지 컴포넌트는 다시 렌더링 하지 않게 되어 보다 좋은 렌더링 성능을 제공할 수 있게 된다.
- 특히 반복 렌더링하는 부분은 반드시 별도의 컴포넌트로 작성하는것이 좋다.
■ 관리해야 할 데이터
- todo는 InputTodo 컴포넌트의 로컬 데이터로 정의한다.
- 왜냐하면, 데이터가 관리할 만큼 중요하지 않으므로 데이터의 생명주기 관리가 필요하지 않기 때문이다.
{
todoList : [
{ id : 1, todo : "자전거 타기", completed : false },
{ id : 2, todo : "공원 산책", completed : true },
{ id : 3, todo : "일요일 애견 카페", completed : false },
{ id : 4, todo : "Vue 원고 집필", completed : false },
]
}
■ 메서드 정의
- 메서드 목록
- addTodo(todo)
- deleteTodo(id)
- toggleCompleted(id)
- 수신 이벤트 목록
- add-todo : 전달 인자-todo
- delete-todo : 전달 인자-id
- toggle-completed : 전달 인자-id
- 컴포넌트 목록
* App 컴포넌트
- data : todoList 배열
- methods : addTodo, deleteTodo, toggleCompleted 메서드
- 수신 이벤트 : add-todo, delete-todo, toggle-completed
* InputTodo 컴포넌트
- data : todo 문자열 값
- 발신 이벤트 : add-todo
* TodoList 컴포넌트
- props : todoList 배열(todoList)
- 발신이벤트 : delete-todo(경유), toggle-completed(경유)
- 수신이벤트 : delete-todo(경유), toggle-completed(경유)
* TodoListItem 컴포넌트
- props : todoItem(todoList 배열의 값 하나)
- 발신 이벤트 : delete-todo, toggle-completed
■ 프로젝트 생성
- todolist-app 프로젝트 생성
npm init vue todolist-app
cd todolist-app
npm install
npm install bootstrap@5
■ 기본 설정
- main.js -> 변경
import { createApp } from 'vue'
import App from './App.vue'
import 'bootstrap/dist/css/bootstrap.css'
import './assets/main.css'
createApp(App).mount('#app')
- src/assets/main.css -> 추가
body { margin : 0; padding : 0; font-family: sans-serif; }
.title { text-align: center; font-weight: bold; font-size: 20pt;}
.todo-done { text-decoration: line-through;}
.container { padding: 10px 10px 10px 10px; }
.panel-borderless { border : 0; box-shadow: none;}
.pointer {cursor: pointer;}
■ 코드 작성
- src/App.vue 작성
<template>
<div id="app" class="container">
<div class="card card-body bg-light">
<div class="title">:: Todolist App</div>
</div>
<div class="card card-default card-borderless">
<div class="card-body">
<InputTodo @add-todo="addTodo" />
<TodoList :todoList="todoList" @delete-todo="deleteTodo"
@toggle-completed="toggleCompleted" />
</div>
</div>
</div>
</template>
<script>
import TodoList from './components/TodoList.vue'
import InputTodo from './components/InputTodo.vue'
let ts = new Date().getTime()
export default {
name : "App",
components : {InputTodo, TodoList},
data() {
return {
todoList : [
{ id: ts, todo:"Vue 공부하기", completed: false },
{ id: ts+1, todo:"Vue 실습 하기", completed: false },
{ id: ts+2, todo:"Vue 프로젝트 하기", completed: false },
{ id: ts+3, todo:"Spring Boot 공부하기", completed: false },
]
}
},
methods : {
addTodo(todo) {
if (todo.length >= 2) {
this.todoList.push({ id : new Date().getTime(), todo: todo, completed : false});
}
},
deleteTodo(id) {
let index = this.todoList.findIndex((item) => id === item.id);
this.todoList.splice(index, 1);
},
toggleCompleted(id) {
let index = this.todoList.findIndex((item) => id === item.id);
this.todoList[index].completed = !this.todoList[index].completed;
}
}
}
</script>
- src/components/InputTodo.vue 작성
<template>
<div class="row mb-3">
<div class="col">
<div class="input-group">
<input id="msg" type="text" class="form-control" name="msg"
placeholder="할일을 여기에 입력하세요!" v-model.trim="todo"
@keyup.enter="addTodoHandler" />
<span class="btn btn-primary input-group-addon"
@click="addTodoHandler">추가</span>
</div>
</div>
</div>
</template>
<script>
export default {
name : "InputTodo",
data() {
return { todo : "" }
},
emits : ["add-todo"],
methods : {
addTodoHandler() {
if (this.todo.length >= 3) {
this.$emit('add-todo', this.todo);
this.todo = "";
}
}
},
}
</script>
- src/components/TodoList.vue
<template>
<div class="row">
<div class="col">
<ul class="list-group">
<TodoListItem v-for="todoItem in todoList" :key="todoItem.id"
:todoItem="todoItem" @delete-todo="$emit('delete-todo', $event)"
@toggle-completed="$emit('toggle-completed', $event)" />
</ul>
</div>
</div>
</template>
<script>
import TodoListItem from './TodoListItem.vue'
export default {
name : "TodoList",
components : { TodoListItem },
props : {
todoList : { type : Array, required:true }
},
emits : ["delete-todo", "toggle-completed"],
}
</script>
- src/components/TodoListItem.vue
<template>
<li class="list-group-item"
:class="{ 'list-group-item-success' : todoItem.completed }"
@click="$emit('toggle-completed', todoItem.id)" >
<span class="pointer" :class="{ 'todo-done' : todoItem.completed }">
{{ todoItem.todo }} {{ todoItem.completed ? "(완료)" : "" }}
</span>
<span class="float-end badge bg-secondary pointer"
@click.stop="$emit('delete-todo', todoItem.id)">삭제</span>
</li>
</template>
<script>
export default {
name : "TodoListItem",
props : {
todoItem : { type : Object, required : true }
},
emits : [ "delete-todo", "toggle-completed"],
}
</script>
- 실행 결과
728x90
'Vue.js' 카테고리의 다른 글
Vue.js 3.0 STEP 6 - 단일 파일 컴포넌트 (2) | 2024.01.25 |
---|---|
Vue.js 3.0 STEP 5 - TodoList (0) | 2024.01.25 |
Vue.js 3.0 STEP 4 - 스타일 적용 (0) | 2024.01.25 |
Vue.js 3.0 STEP 3 - 이벤트 처리 (0) | 2024.01.18 |
Vue.js 3.0 STEP 2 - Vue 인스턴스 (0) | 2024.01.18 |