iT邦幫忙

2024 iThome 鐵人賽

DAY 28
0
JavaScript

TypeScript 初學者也能看的學習指南系列 第 28

TypeScript 初學者也能看的學習指南 28 - Vue3 + TS 實作簡易 To Do List (Part 2)

  • 分享至 

  • xImage
  •  

https://ithelp.ithome.com.tw/upload/images/20241009/20149362gDoPfiX5YA.png

昨天我們建立好了環境和型別檔後,今天來繼續實作新增、刪除、修改 todo list 的功能

文章傳送門
🔗 Day27 - Vue3 + TS 實作簡易 To Do List (Part 1)

大綱

內容 重點摘要
建立 store import type
建立元件 Utility Type - Omit

建立 store

建議初步可以先把原本的寫法寫出來,再加上 ts 型別

完整 code

import { TodoStatus,  type TodoStructure } from '@/types';
import { reactive, computed } from 'vue';


interface TodoStore {
  [TodoStatus.Pending]: TodoStructure[];
  [TodoStatus.InProgress]: TodoStructure[];
  [TodoStatus.Completed]: TodoStructure[];
}

const defaultList = {
  [TodoStatus.Pending]: [
    // 先預設加入一個待辦
    {
      id: 1,
      title: "寫鐵人賽",
      description: "主題:實作",
      status: TodoStatus.Pending,
    },
  ],
  [TodoStatus.InProgress]: [],
  [TodoStatus.Completed]: [],
}

const todoStore = reactive<TodoStore>(defaultList);


// 取得
const getTodoByStatus = () => (status: TodoStatus) => {
  return computed(() => todoStore[status]);
}
// 新增
const createTodo = (todo: TodoStructure) => {
  todoStore[todo.status].push(todo);
}
// 刪除
const deleteTodo = (todo: TodoStructure) => {
  todoStore[todo.status] = todoStore[todo.status].filter(item => item.id !== todo.id);
}
// 修改
const updateTodo = (todo: TodoStructure, newStatus: TodoStatus) => {
  todo.status = newStatus;
}

export {
  getTodoByStatus,
  createTodo,
  deleteTodo
}

在這段可以看到一個特殊語法 import type
這裡的 type 寫在 {} 裡面,雖然跟寫在外面(import type)語法不同,但其實用意相同

import { TodoStatus,  type TodoStructure } from '@/types';

Typescript 3.8 版本新增了 import type,使用 import type 的用意是更明確指出從某個模組導入的是型別(Type),而非值(Value)。這樣做有幾個主要好處:

  1. 清晰性
    能夠清楚的表示導入的內容只用於靜態型別檢查,並不會被編譯成 JavaScript,讓維護和開發的人能更快速知道這些導入只用於 TypeScript 的型別系統中

  2. 提升編譯效率
    TypeScript 編譯器知道 import type 只用於類型檢查,並不會被編譯成 JavaScript。避免了不必要的引入,有助於減少最終打包文件的大小,尤其是在大型專案中,每一點的效能提升都很寶貴呀~

  3. 避免名稱衝突
    使用 import type 可以避免在導入型別時與局部變數或其他導入的值產生名稱衝突。減少了作用域污染的可能

想看 import 和 import type 的差別,可看 這篇文章

建立元件

主要區分三個元件

  1. CreateTodo
  2. TodoGroup
  3. TodoList

CreateTodo.vue

這邊只著重寫 ts 有關的內容,想看完整程式碼點這

顧名思義,這個檔案就是在處理新增 todo 的功能啦~

<script setup lang="ts">
import { reactive, ref } from "vue";
import type { TodoStructure, TodoStatus } from "@/types";
import useTodos from "@/store/useTodos";

const shouldDisplayForm = ref(false);
const { createTodo } = useTodos();

// ✅ 關注這段
interface Props {
  status: TodoStatus;
}
const props = defineProps<Props>();

// ✅ 關注這段
const newTodo = reactive<Omit<TodoStructure, "id">>({
  title: "",
  description: "",
  status: props.status,
});

const resetForm = () => {
  shouldDisplayForm.value = false;
  newTodo.title = "";
  newTodo.description = "";
};

const handleOnSubmit = () => {
  createTodo({
    id: Math.random() * 1000,
    ...newTodo,
  });
  resetForm();
};
</script>

其中這段使用到了 utility type
Omit 是一個工具型別(Utility Type),幫助我們在不修改原始的型別下,排除一些特定的屬性,建立一個新的型別

Omit 型別接收兩個參數:
第一個參數是傳入的 Type,是物件類型
第二個參數第二個參數是要忽略的鍵(key)

const newTodo = reactive<Omit<TodoStructure, "id">>({
  title: "",
  description: "",
  status: props.status,
});

TodoGroup.vue

TodoGroup 的內容稍多,一樣只著重講 ts 部分,想看完整程式碼點這

首先因為每個待辦有拖曳功能,所以來安裝 Vue.Draggable 套件
https://ithelp.ithome.com.tw/upload/images/20241008/20149362LIcY0Mrljg.png

<script setup lang="ts">
import { TodoStatus } from "@/types";
import Draggable from "vuedraggable";
import useTodos from "@/store/useTodos";
import CreateTodo from "@/components/CreateTodo.vue";

interface Props {
  status: TodoStatus;
}

const props = defineProps<Props>();

const {
  getTodoByStatus,
  deleteTodo,
  updateTodo
} = useTodos();

const todoList = getTodoByStatus(props.status);

const groupLabel = {
  [TodoStatus.Pending]: "Pending",
  [TodoStatus.InProgress]: "In Progress",
  [TodoStatus.Completed]: "Completed",
};

const onDraggableChange = (payload: any) => {
  if (payload?.added?.element?.status) {
    updateTodo(payload?.added?.element, props.status);
  }
};
</script>

TodoList.vue

TodoList 的完整內容都在下面,做的事很單純,只是引入 TodoGroup,並帶入 status
Todo List 有三種狀態:未完成、進行中、已完成

<template>
  <div class="groups-wrapper">
    <TodoGroup :status="TodoStatus.Pending" />
    <TodoGroup :status="TodoStatus.InProgress" />
    <TodoGroup :status="TodoStatus.Completed" />
  </div>
</template>

<script setup lang="ts">
import { TodoStatus } from "@/types";
import TodoGroup from "@/components/TodoGroup.vue";
</script>

github repo: https://github.com/hangineer/todo-app

References


上一篇
TypeScript 初學者也能看的學習指南 27 - Vue3 + TS 實作簡易 To Do List (Part 1)
下一篇
TypeScript 初學者也能看的學習指南 29 - 讓 TypeHero 成為你學習的助力
系列文
TypeScript 初學者也能看的學習指南30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言