Vue
[Vue] Vue3 TodoList
Vue 3로 TodoList를 만들었습니다.
Vue 3는 여기까지 정리하고, 다음 글부터는 Vue 2에 TypeScript를 적용하는 방법을 공부할 예정입니다.
gh-pages : https://kimdongeun12.github.io/vue3-practice/ github : https://github.com/kimdongeun12/vue3-practice
폴더 구조
├─ src
│ ├─ App.vue
│ ├─ main.ts
│ └─ views
│ └─ todo
│ ├─ TodoList.vue // Todo의 View
│ ├─ TodoListWrap.vue // Todo의 List를 담고 있음
│ └─ TodoListWrite.vue // 리스트 작성 , 수정을 담당
폴더 구조는 위와 같습니다.
TodoList.vue
<template>
<v-container>
<TodoListWrap
:Todolists="Todolists"
@isChecked="isChecked"
@deleteTodo="deleteTodo"
@showDialog="showDialog"
/>
<v-btn
class="floating"
icon="mdi-pencil"
size="small"
@click="showDialog()"
/>
<v-dialog v-model="dialog">
<TodoListWrite
:dialog="dialog"
:edits="edits"
:editTodo="edits.edit ? Todolists[edits.idx] : {}"
@hideDialog="hideDialog"
@submitDialog="submitDialog"
@editSubmitDialog="editSubmitDialog"
/>
</v-dialog>
</v-container>
</template>
export default defineComponent({
...
methods: {
showDialog(idx?: number) {
this.dialog = true;
if (idx !== undefined) {
this.edits.idx = idx;
this.edits.edit = true;
} else {
this.edits.idx = 0;
this.edits.edit = false;
}
}
},
...
});
</script>
<style lang="scss" scoped>
.floating {
position: fixed;
right: 24px;
bottom: 24px;
}
</style>
TodoListWrap.vue
<template>
<div class="listsWrap">
<v-card
class="mx-auto my-list"
v-for="(item, idx) in Todolists"
:key="idx"
:class="{ 'mx-auto active': item?.checked }"
width="400"
>
<template v-slot:title>
<v-btn
variant="text"
icon="mdi-check"
size="small"
@click="$emit('isChecked', idx)"
/>
{{ item?.title }}
</template>
<v-card-text> {{ item?.contents }} </v-card-text>
<v-card-actions class="justify-end">
<v-btn variant="outlined" @click="$emit('showDialog', idx)">
수정
</v-btn>
<v-btn variant="outlined" @click="$emit('deleteTodo', idx)">
삭제
</v-btn>
</v-card-actions>
</v-card>
<div class="text-center" v-if="Todolists?.length === 0">
할 일 목록이 없습니다.<br />
우측 아래 버튼을 클릭하여 추가해주세요.
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from "vue";
export default defineComponent({
name: "TodoListWrap",
components: {},
props: {
Todolists: Object,
},
data() {
return {};
},
});
</script>
<style lang="scss" scoped>
.listsWrap {
display: block;
& > div + div {
margin-top: 20px;
}
.active:deep .v-card-title .v-btn__content {
color: #ff0000;
}
}
</style>
작업하면서 느낀 점이 두 가지 있습니다. 첫째, 스타일은 대부분 scoped로 작성하는데 Vuetify 컴포넌트에는 스타일이 적용되지 않을 때가 있습니다. 이때 :deep을 사용하면 해결됩니다. 저는 v-card 안의 체크 아이콘 색상을 바꿀 때 사용했습니다.
둘째, props는 React와 비슷하면서도 다릅니다.
:속성명으로 바인딩해 props를 전달하고, 이벤트처럼 함수를 넘길 때는
@이벤트명으로 전달합니다. 자식에서는
emit('이벤트명', 전달할 값)으로 부모에게 알립니다.
아직 양방향 바인딩의 장점을 깊이 체감하지는 못했지만, 단방향으로만 데이터를 전달하는 React보다 처음 입문하기에는 Vue가 더 편하다고 느꼈습니다.
전체 코드는 위 GitHub 저장소에서 확인할 수 있습니다.
댓글 0