[新手入门]todolist增删改查:vue3+ts版本!

简介: 【10月更文挑战第15天】[新手入门]todolist增删改查:vue3+ts版本!

项目需求

前端的待办事项应用(To-Do List)是一个经典的练习项目,非常适合初学者。这个项目虽然看似简单,但涵盖了许多前端开发的核心概念和技术。那么,本文就实现简单的增删改查功能,实现效果如下图:

image.png

使用脚手架安装相关依赖

使用vueCli 安装预设的vuex+ts+less+router

## 查看@vue/cli版本,确保@vue/cli版本在4.5.0以上
vue --version

## 安装或者升级你的@vue/cli
npm install -g @vue/cli

## 创建
vue create vue3_todo          //create vue3_cli_todo为自定义文件名

按方向键 ↓,选择Manually select features,enter

手动配置:是我们所需要的面向生产的项目,提供可选功能的 npm 包

手动配置,根据你需要用方向键选择(按 “空格键”选择/取消选择,A键全选/取消全选,i反选)对应功能

  1. ? Check the features needed for your project: (Press to select, to toggle all, to invert selection)
  2. ( ) TypeScript // JavaScript的一个超集(添加了可选的静态类型和基于类的面向对象编程:类型批注和编译时类型检查、类、接口、模块、lambda 函数)

  3. ( ) Progressive Web App (PWA) Support // 渐进式Web应用程序
  4. () Router // vue-router(vue路由)
  5. () Vuex // vuex(vue的状态管理模式)
  6. () CSS Pre-processors // CSS 预处理器(如:less、sass)
  7. () Linter / Formatter // 代码风格检查和格式化(如:ESlint)
  8. () Unit Testing // 单元测试(unit tests)
  9. () E2E Testing // e2e(end to end) 测试

我们选择如下

确认enter后,需要选择vue版本,选择3.x的版本然后继续确认

然后如下配置

是否使用class风格的组件语法:Use class-style component syntax?输入N

目录解析

├─ babel.config.js
├─ package-lock.json
├─ package.json
├─ public
│  ├─ favicon.ico
│  └─ index.html
├─ README.md
├─ src
│  ├─ App.vue
│  ├─ assets
│  ├─ components
│  │  ├─ TodoInput               //输入框组件
│  │  │  └─ index.vue  
│  │  └─ TodoList
│  │     ├─ index.vue            //数据组件
│  │     └─ Item.vue
│  ├─ hooks
│  │  └─ index.ts                //公用的hook函数
│  ├─ main.ts                    //入口文件
│  ├─ shims-vue.d.ts             //让Vue识别TS的文件
│  ├─ store
│  │  ├─ actions.ts
│  │  ├─ actionTypes.ts          //派发事件
│  │  ├─ index.ts
│  │  ├─ mutations.ts
│  │  └─ state.ts
│  └─ typings
│     └─ index.ts                //定义vuex中的数据类型,方便使用TS
└─ tsconfig.json

实现路线简析

入口文件

src\main.ts

import {
     createApp } from 'vue' // 引入 Vue 3 中的 createApp 函数
import App from './App.vue' // 引入根组件 App.vue
import store from './store' // 引入 Vuex store
import './assets/style/reset.less' // 引入样式重置文件(使用 Less 预处理器)

// 创建 Vue 应用实例
const app  = createApp(App)

// 使用 Vuex store 插件,并将应用挂载到 id 为 'app' 的 DOM 元素上
app.use(store).mount('#app')

store

store一个基于Vuex的状态管理模块,我们将使用它管理待办事项列表(To-Do List)的状态。通过分离actions、mutations和state,这使代码更加模块化和可维护。

│  ├─ store
│  │  ├─ actions.ts
│  │  ├─ index.ts
│  │  ├─ mutations.ts
│  │  └─ state.ts

src\store\actions.ts

import {
     IState, IList } from "@/typings"
import {
     Commit } from "vuex"
//context对象声明
interface Icontext {
    
    commit: Commit,
    state: IState
}
export default {
    
    //获取数据列表
    // query包含两个参数,context对象,和payload参数
    // context对象包含 commit和state两个参数
    query(context: Icontext,todoList: IList[]): void {
    
        context.commit("query",todoList)
    }, 
}

定义了一个actions对象,用于处理异步操作或复杂的业务逻辑。

query方法接收contexttodoList,使用context.commit调用mutations中的query方法,更新状态。

src\store\mutations.ts

import {
     IState, IList,} from "@/typings"

export default {
    
    query(state: IState, todoList: IList[]): void{
    
        state.list = todoList
    }, 
}

定义了一个mutations对象,用于同步地修改状态。

query方法接收当前状态state和一个新的待办事项列表todoList,并将新的列表赋值给状态中的list

src\store\state.ts

import {
     IState } from "@/typings"
// 这里使用了类型断言的方式对导出的对象进行了声明: <类型>值
export default <IState> {
    
    list: []
}

定义了应用的初始状态。

list初始化为空数组,表示初始状态下没有待办事项。
src\store\index.ts

import {
     createStore } from 'vuex'
import state from './state'
import mutations from './mutations'
import actions from './actions'
export default createStore({
    
  state,
  mutations,
  actions,
})

使用Vuex的createStore方法创建一个新的Store实例。

将定义好的statemutationsactions模块整合在一起,形成一个完整的Vuex Store。

src\typings\index.ts

//接口返回的数据列表类型声明
interface IList {
    
    id: number,
    content: string,
    status:TODO_STATUS,
    createTime: number
}
//state类型定义
interface IState {
    
    list: IList[]
}
enum  TODO_STATUS {
    
    WILLDO = 'willdo',
    DOING = 'doing',
    FINISHED = 'finished'
}
export{
    
    TODO_STATUS,
    IState,
    IList
}

定义了类型接口和枚举,确保在操作状态时有明确的类型约束。

IList接口描述了每个待办事项的结构。

IState接口描述了Vuex状态中list的结构。

TODO_STATUS枚举定义了待办事项的三种状态:WILLDODOINGFINISHED

App.vue

<template>
  <div class="wrapper">
    <TodoInput></TodoInput>
    <TodoList :todoList="todoList"></TodoList>
  </div>
</template>

<script lang="ts">
import {
      computed, defineComponent, onMounted } from 'vue';
import {
      Store, useStore } from 'vuex';
import TodoInput from './components/TodoInput/index.vue';
import TodoList from './components/TodoList/index.vue'
import {
      IUseTodo, useTodo } from './hooks';
//defineComponent是为了帮助Ts语法有更好的类型提示
export default defineComponent({
     
  name: 'App',
  components: {
     
    TodoList,TodoInput
  },
  setup () {
     
    //useTodo是封装了增删改查函数的一个总函数;IUseTodo是函数定义
    const {
      query }: IUseTodo = useTodo();
    //获取vuex的store对象
    const store: Store<any> = useStore()
    onMounted( () => {
     
      //列表初始化
      query();
    })
    return {
     
      todoList : computed(() => store.state.list)
    }
  }

});
</script>

<style lang="less">

</style>

上面的代码是一个Vue 3组件,用于管理和显示一个待办事项列表。其中

  • <TodoInput>组件用于用户输入
  • <TodoList>组件用于列表显示

    hooks

//用于封装组件的公用方法
import {
     Store, useStore } from "vuex"
import axios from "../proxy";
//排序函数
function compare(property : any) {
    
    return function (value1: any, value2: any) {
    
      let v1 = value1[property];
      let v2 = value2[property];
      return v2 - v1
    }
  }
//定义useTodo ()函数接口
export interface IUseTodo {
    
    query: () => void,
    add: (value: string) => void,
    remove: (id: number) => void,
    edit: (item: object) => void,
}
//封装增删改查相关函数
function useTodo (): IUseTodo {
    
    //使用vuex中的store
    const store: Store<any> = useStore(); 
    //1.查询数据 
    function query() {
    
        axios.getList().then((res)=>{
    
            let data = res.data
            data.forEach((res: {
     status: any; }) => {
    
                switch(res.status){
    
                    case 1:
                        res.status = 'willdo'
                        break;
                    case 0:
                        res.status = 'doing'
                        break;
                    case 2:
                        res.status = 'finished'
                        break;
                    default:
                        return
                }
            });
            data = data.sort(compare('createTime'))
            store.dispatch("query", data)
        })
    }
    //2.增加数据   ----->函数输入字符串数据,没有返回值
    function add(value: string): void {
    
        let data = {
    
            content: value,
        }
        axios.addList(data).then(res => {
    
            //数据增加成功后,重新获取数据列表
            query()
        })
    }
    //3.删除数据 
    function remove(id: string | number): void {
    
        axios.deleteList({
     id }).then(res => {
    
            //根据id进行数据删除
            query()
        })   
    }
    //4.更改数据
    function edit(item: any): void {
    
        let data = {
    
            id:item.id,
            content:item.content,
            status:2
        }
        axios.editList(data).then(res => {
    
            query()
        })   
    }
    return {
     query, add,remove, edit }
}
export {
    
    useTodo
}

使用vue3提供的hooks函数将代码逻辑进行了抽象,逻辑更加清晰

proxy

import axios from 'axios';
const baseUrl = 'http://47.97.50.125:8989/todoList/'

const httpUrl = {
    
    getList:baseUrl + 'list/?token=',
    editList:baseUrl + 'editList',
    deleteList:baseUrl + 'deleteList',
    addList:baseUrl + 'addList',
}

//获取列表
function getList(){
    
    return axios.get(httpUrl.getList)
}
//编辑列表
function editList(editData: object) {
    
    return axios.post(httpUrl.editList,editData)
}
//删除列表
function deleteList(deleteData: object) {
    
    return axios.post(httpUrl.deleteList,deleteData)
}
//新增列表
function addList(addData: object) {
    
    return axios.post(httpUrl.addList,addData)
}

export default{
    
    getList,
    editList,
    deleteList,
    addList
}

上述代码片段展示了一个用于与后端待办事项(To-Do List)API进行交互的模块。这个模块使用axios库来发送HTTP请求,并且定义了几个方法来处理不同的操作,包括获取列表、编辑列表、删除列表和新增列表。

TodoInput

src\components\TodoInput\index.vue:

<template>
    <div>
        <div class="wrap" @mouseleave="clearVisible = false">
          <input type="text" v-model="todoValue"  @keyup="setTodoValue" @focus="clearVisible = true" />
          <span class="ensure" @click="ensure"> 确认</span>
          <span class="clerar" v-show="clearVisible || todoValue" @click="todoValue = ''"><img src="../../assets/cleaer.svg" alt=""></span>
        </div> 
    </div>
</template>
<script lang="ts">
import{
      defineComponent ,ref}from 'vue';
import {
      IUseTodo, useTodo } from '../../hooks'
export default defineComponent({
     
    name: "TodoInput",
    setup() {
     

        const todoValue =ref<string>('');
        let clearVisible = ref<Boolean>(false)
        const {
      add }: IUseTodo = useTodo()
        //增  键盘事件
        const setTodoValue = (e: KeyboardEvent): void =>{
     
            if(e.keyCode === 13 && todoValue.value.trim().length){
     
                //设置数据
                add(todoValue.value)
                //清空数据
                todoValue.value = ''
            }
        }
        //增  按钮事件
        const ensure = (): void =>{
     
            if(todoValue.value.trim().length){
     
                //设置数据
                add(todoValue.value)
                //清空数据
                todoValue.value = ''
            }
        }
        return {
     
            todoValue,
            clearVisible,
            setTodoValue,
            ensure
        }
    }
})
</script>
<style lang="less" scoped>
.wrap{
     
    display: flex;
    width: 700px;
    margin: 20px auto;
    justify-content: center;
    position: relative;
    input{
     
        width: 590px;
        height: 40px;
        color: #909399;
        font-size: 16px;
        border: 1px solid rgb(196 199 206);
        border-radius: 10px 0 0 10px;
        padding: 0 10px;
    &:focus{
     
        border: none;
        box-shadow:  0 0 0 1px rgb(78 110 242),0 0 0 1px #BDE7FF;
        outline:none;
        transform: translateY(1px);
         height: 38px;
    }
    &::after{
     
        content: '2222';
        display: block;
        width: 10px;
        height: 10px;
        background-color: aqua;
    }
   }
    .ensure{
     
    cursor: pointer;
        width: 112px;
        display: inline-block;
        height: 40px;
        line-height: 40px;
        background-color: rgb(78 110 242);
        border-radius: 0 10px 10px 0;
        font-size: 17px;
        color:rgb(255 255 255); 
        text-align: center;
    }
    .clerar{
     
        position: absolute;
        height: 40px;
        width: 40px;
        right: 112px;
        display: flex;
        align-items: center;
        justify-content: center;
        line-height: 40px;
        img{
     
            width: 60%;
            height: auto;
        }
    }
}

</style>

TodoList

<template>
    <div>
        <todo-item
            v-for="item in todoList"
            :key = 'item.id'
            :item="item"
            @remove = "remove"
            @edit = "edit"
            @add = "add"
        ></todo-item>   
    </div>
</template>
<script lang="ts">
import {
      IUseTodo, useTodo } from '@/hooks';
import {
      IList } from '@/typings';
import{
      defineComponent, PropType }from 'vue';
import TodoItem from './Item.vue'
export default defineComponent({
     
    name: "TodoList",
    props: {
     
        //类型断言
        todoList: Array as PropType< IList[] >
    },
    components: {
     
        TodoItem
    },
    setup (props) {
     
        const {
      remove, add, edit }: IUseTodo = useTodo();
        return{
     
            remove, 
            add, 
            edit,
        }
    }
})
</script>
<style lang="less" scoped>

</style>
相关文章
|
1月前
|
Java 数据库连接 测试技术
SpringBoot入门(4) - 添加内存数据库H2
SpringBoot入门(4) - 添加内存数据库H2
47 4
SpringBoot入门(4) - 添加内存数据库H2
|
2月前
|
Java 数据库连接 测试技术
SpringBoot入门(4) - 添加内存数据库H2
SpringBoot入门(4) - 添加内存数据库H2
31 2
SpringBoot入门(4) - 添加内存数据库H2
|
27天前
|
Java 数据库连接 测试技术
SpringBoot入门(4) - 添加内存数据库H2
SpringBoot入门(4) - 添加内存数据库H2
65 13
|
21天前
|
Java 数据库连接 测试技术
SpringBoot入门(4) - 添加内存数据库H2
SpringBoot入门(4) - 添加内存数据库H2
36 4
|
2月前
|
存储 人工智能 Java
Neo4j从入门到精通:打造高效知识图谱数据库 | AI应用开发
在大数据和人工智能时代,知识图谱作为一种高效的数据表示和查询方式,逐渐受到广泛关注。本文从入门到精通,详细介绍知识图谱及其存储工具Neo4j,涵盖知识图谱的介绍、Neo4j的特点、安装步骤、使用方法(创建、查询)及Cypher查询语言的详细讲解。通过本文,读者将全面了解如何利用Neo4j处理复杂关系数据。【10月更文挑战第14天】
122 6
|
1月前
|
SQL 关系型数据库 数据库连接
"Nacos 2.1.0版本数据库配置写入难题破解攻略:一步步教你排查连接、权限和配置问题,重启服务轻松解决!"
【10月更文挑战第23天】在使用Nacos 2.1.0版本时,可能会遇到无法将配置信息写入数据库的问题。本文将引导你逐步解决这一问题,包括检查数据库连接、用户权限、Nacos配置文件,并提供示例代码和详细步骤。通过这些方法,你可以有效解决配置写入失败的问题。
56 0
|
2月前
|
XML 缓存 数据库
Discuz! X3.0 版本的数据库字典
Discuz! X3.0 版本的数据库字典
55 0
|
2月前
|
SQL 数据库
SQL数据库基础语法入门
[link](http://www.vvo.net.cn/post/082935.html)
|
19天前
|
SQL 关系型数据库 MySQL
12 PHP配置数据库MySQL
路老师分享了PHP操作MySQL数据库的方法,包括安装并连接MySQL服务器、选择数据库、执行SQL语句(如插入、更新、删除和查询),以及将结果集返回到数组。通过具体示例代码,详细介绍了每一步的操作流程,帮助读者快速入门PHP与MySQL的交互。
34 1
|
22天前
|
SQL 关系型数据库 MySQL
go语言数据库中mysql驱动安装
【11月更文挑战第2天】
36 4