vite+vue3+ts简单例子todolist

vue3发布以来备受瞩目,与之起来的vite都是倍感神秘,在这里我们用一个todolist的小例子,来揭秘vue3+vite的面纱。
本文是在《Vite + Vue3 初体验 —— Vue3 篇》的启发下写的,由于原文的代码和过程有一些需要注意的坑点,在这里我们会把步骤详细的整理以及简单的优化

创建项目

使用vite创建

npm init vite@latest

输入项目名 如 vue-todolist
在这里插入图片描述
vite可以构建多种框架的项目,这里选用vue
在这里插入图片描述
选用vue-ts的组合,如果你对ts还不熟,需要抓紧时间上车了
在这里插入图片描述

这样项目就创建好了,剩下就是装依赖和运行项目了
在这里插入图片描述

开始编写 todolist

为了项目开起来稍微美观一点,这里使用ant-design-vue的UI框架,另外框架的组件按需引入还需要使用(unplugin-vue-components)。
在项目里边安装:

// 按需引入插件
npm install unplugin-vue-components -D
// UI框架
npm install ant-design-vue@next --save

**这里可以用npm也可以用yarn,根据个人情况使用,本文全部使用npm方式

配置按需引入

上边安装了按需引入的插件,还需要进行配置,在vite.config.js中配置
在这里插入图片描述

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import Components from "unplugin-vue-components/vite"
import { AntDesignVueResolver } from "unplugin-vue-components/resolvers";

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    Components({
      resolvers: [
        AntDesignVueResolver()
      ]
    })
  ]
})

然后在main.ts中引入ant-design-vue的样式
在这里插入图片描述

基本结构

在components中创建TodoList.vue文件,并引入App.vue中,在TodoList.vue中编写基本todolist的结构:
TodoList.vue

<template>
  <div class="todo-list-container" >
    <div class="todo-wrapper" >
      <input class="todo-input" placeholder="请输入待办项" />
      <ul class="todo-list" >
        <li class="todo-item" >
          <span>Todo Items</span>
          <div class="operator-list" >
            <DeleteOutlined />
            <CheckOutlined />
            <ToTopOutlined />
          </div>
        </li>
        <li class="todo-item" >
          <span>Todo Items</span>
          <div class="operator-list" >
            <DeleteOutlined />
            <CheckOutlined />
            <ToTopOutlined />
          </div>
        </li>
        <li class="todo-item todo-completed" >
          <span>Todo Items</span>
          <div class="operator-list" >
            <CheckCircleFilled />
          </div>
        </li>
      </ul>
    </div>
  </div>
</template>

<script setup lang="ts">
// Input组件
import {Input} from 'ant-design-vue';
// 图标组件 
import { DeleteOutlined, CheckOutlined, CheckCircleFilled, ToTopOutlined } from "@ant-design/icons-vue";
</script>

<style scoped lang="less">
*{
  padding: 0;
  margin: 0;
}
.todo-list-container {
  width: 100vw;
  height: 100vh;
  box-sizing: border-box;
  padding-top: 100px;
  background: linear-gradient(rgba(93, 190, 129, 0.02), rgba(125, 185, 222, 0.02));
  display: flex;
  justify-content: center;
  .todo-wrapper {
    width: 60vw;
    .todo-input {
      width: 100%;
      height: 50px;
      border: 2px solid rgba(255, 177, 27, 0.5);
      border-radius: 5px;
      padding-left: 10px;
      font-size: 18px;
      color: #f05e1c;
      &::placeholder {
        color: #f05e1c;
        opacity: 0.4;
      }
      &:hover, &:focus {
        border-color: #ffb11b;
        box-shadow: 0 0 0 2px rgba(255, 177, 27, 0.2);
        outline: none;
      }
    }
    .todo-list {
      margin-top: 20px;
      list-style: none;
      .todo-item {
        margin-bottom: 10px;
        padding: 15px 10px;
        box-sizing: border-box;
        border-bottom: 2px solid rgba(255, 177, 27, 0.3);
        border-radius: 5px;
        font-size: 16px;
        color: #f05e1c;
        cursor: pointer;
        display: flex;
        justify-content: space-between;
        align-items: center;
        transition: all .5s;
        &:hover {
          box-shadow: 0 0 5px 8px rgba(255, 177, 27, 0.2);
          border-bottom: 2px solid transparent;
        }
        // 完成列表样式
        &.todo-completed {
          color: rgba(199,199,199,1);
          border-bottom-color: rgba(199,199,199,0.4);
          &:hover {
            box-shadow: none;
            border-bottom-color: rgba(199,199,199,0.4);
          }
        }
        // 置顶列表样式
        &.todo-top {
          box-shadow: none;
          background-color: #f05e1c;
          color: #fff;
        }

        // 操作列表
        .operator-list {
          display: flex;
          justify-content: flex-start;
          align-items: center;
          &:first-child {
            margin-right: 10px;
          }
        }
      }
    }
  }
}
</style>

这里使用了less,vite搭建这套脚手架中,已经配置了less/sass,只需要安装即可:

npm install -D less less-loader

然后运行项目:
在这里插入图片描述

会得到上边的结构,如果你的有差异,不要慌张耐心查看自己的问题以及代码是否和文中一致,如果实在解决不了可以留言,也可以加博主微信文末有介绍。

然后就是逻辑部分的代码

先上完整的TodoList.vue组件代码

<template>
  <div class="todo-list-container" >
    <div class="todo-wrapper" >
      <input 
        class="todo-input" 
        placeholder="请输入待办项" 
        v-model="todoText"
        @keydown.enter="addTodoList"
      />
      <ul class="todo-list" >
        <li 
          v-for="(item, index) in todoList"
          class="todo-item" 
          :class="{'todo-completed': item.is_completed, 'todo-top': item.is_top}"
        >
          <span>{{item.title}}</span>
          <div class="operator-list" >
            <CheckCircleFilled v-if="item.is_completed"/>
            <ToTopOutlined v-if="!item.is_completed" @click="topTodoList(index)" />
            <DeleteOutlined v-if="!item.is_completed" @click="deleteTodoList(index)" />
            <CheckOutlined v-if="!item.is_completed" @click="completedTodoList(index)" />
          </div>
        </li>
      </ul>
    </div>
  </div>
</template>

<script setup lang="ts">
// Input组件
import {Input} from 'ant-design-vue';
// 图标组件 
import { DeleteOutlined, CheckOutlined, CheckCircleFilled, ToTopOutlined } from "@ant-design/icons-vue";
import { ref } from 'vue'

// 声明存储数据数组
// let todoList = ref<{
//   title: string,
//   is_completed: Boolean,
//   is_top: Boolean
// }[]>([]);
// 上边采用了ts泛型结合的写法,也可以将类型分开
interface TodoType {
  title: string,
  is_completed: boolean,
  is_top: boolean
};
let todoList = ref<TodoType[]>([]);
// 这里声明一个对数组进行排序的方法,在增删改查后对list进行排序
const todoListSort = (list: TodoType[]):TodoType[] => {
  // 置顶的列表
  let topList:TodoType[] = [];
  // 完成的列表
  let completedList:TodoType[] = [];
  // 其他列表
  let otherList:TodoType[] = [];
  list.forEach(item => {
    if(item.is_top){
      topList.push(item);
    }else{
      if(item.is_completed){
        completedList.push(item);
      }else{
        otherList.push(item);
      }
    }
  });
  return [...topList, ...otherList, ...completedList];
}

// 创建一个变量,用于输入框的绑定
const todoText = ref('');
// 这里ref看似没有使用泛型,实际上是省略了,上边的等于
// const todoText = ref<string>('');

// 然后是增加list
const addTodoList = () => {
  // 如果input内容为空则不继续执行
  if(!todoText.value) return;
  // 将input的内容添加到todoList
  todoList.value.unshift({
    title: todoText.value,
    is_completed: false,
    is_top: false
  });
  // 添加完成后,清空todoText的值
  todoText.value = '';
  // 插入后排序
  todoList.value = todoListSort(todoList.value);
}

// 删除List
const deleteTodoList = (index:number) => {
  todoList.value.splice(index, 1);
}

// 完成List
const completedTodoList = (index:number) => {
  todoList.value[index].is_completed = true;
  // 修改后排序,将完成的放后边
  todoList.value = todoListSort(todoList.value);
}

// 置顶列表list
const topTodoList = (index:number) => {
  todoList.value[index].is_top = true;
  // 置顶后排序,将完成的放前边
  todoList.value = todoListSort(todoList.value);
}


</script>

<style scoped lang="less">
*{
  padding: 0;
  margin: 0;
}
.todo-list-container {
  width: 100vw;
  height: 100vh;
  box-sizing: border-box;
  padding-top: 100px;
  background: linear-gradient(rgba(93, 190, 129, 0.02), rgba(125, 185, 222, 0.02));
  display: flex;
  justify-content: center;
  .todo-wrapper {
    width: 60vw;
    .todo-input {
      width: 100%;
      height: 50px;
      border: 2px solid rgba(255, 177, 27, 0.5);
      border-radius: 5px;
      padding-left: 10px;
      font-size: 18px;
      color: #f05e1c;
      &::placeholder {
        color: #f05e1c;
        opacity: 0.4;
      }
      &:hover, &:focus {
        border-color: #ffb11b;
        box-shadow: 0 0 0 2px rgba(255, 177, 27, 0.2);
        outline: none;
      }
    }
    .todo-list {
      margin-top: 20px;
      list-style: none;
      .todo-item {
        margin-bottom: 10px;
        padding: 15px 10px;
        box-sizing: border-box;
        border-bottom: 2px solid rgba(255, 177, 27, 0.3);
        border-radius: 5px;
        font-size: 16px;
        color: #f05e1c;
        cursor: pointer;
        display: flex;
        justify-content: space-between;
        align-items: center;
        transition: all .5s;
        &:hover {
          box-shadow: 0 0 5px 8px rgba(255, 177, 27, 0.2);
          border-bottom: 2px solid transparent;
        }
        // 完成列表样式
        &.todo-completed {
          color: rgba(199,199,199,1);
          border-bottom-color: rgba(199,199,199,0.4);
          &:hover {
            box-shadow: none;
            border-bottom-color: rgba(199,199,199,0.4);
          }
        }
        // 置顶列表样式
        &.todo-top {
          box-shadow: none;
          background-color: #f05e1c;
          color: #fff;
        }

        // 操作列表
        .operator-list {
          display: flex;
          justify-content: flex-start;
          align-items: center;
          &:first-child {
            margin-right: 10px;
          }
        }
      }
    }
  }
}
</style>

上边就是完整的代码,增加、完成和置顶使用排序,这里是练习时候为了达到一定的效果编写的,实际应用中要在增加和设置的地方单个操作更好,不然每次都给整个数据排序效率太低。
以上完成后如下图
在这里插入图片描述


以上就是本文全部内容,如对你有帮助欢迎点赞留言
如有疑问可以留言,也可以到QQ群一起探讨:
QQ群1: 657011407, QQ群2: 492593055,也可以到微信找我 shenzhipeng1023

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值