⚡️[前端学习]从0到1做一个Vue风格的Todolist(Vue牛刀小试)⚡️

简介: ⚡️[前端学习]从0到1做一个Vue风格的Todolist(Vue牛刀小试)⚡️

前言

大家好,我是程序员manor,我希望自己能成为国家复兴道路的铺路人,大数据领域的耕耘者,平凡但不甘于平庸的人。

学习vue差不多5天了,终于能写代码,以下代码是我按照B站尚硅谷的案例敲出来的:

https://www.bilibili.com/video/BV1Zy4y1K7SH?p=82&spm_id_from=pageDriver

整体的目录结构:

忽略掉MyList的报红,暂时弄不清楚如果去除 ,并不影响代码运行

先自己找一个目录,并创建一个vue脚手架:

vue create todo

运行项目

npm run serve 

main.js

//引入Vue
import Vue from 'vue'
//引入App
import App from './App.vue'
//关闭Vue的生产提示
Vue.config.productionTip = false
//创建vm
new Vue({
  el:'#app',
  render: h => h(App)
})

App.vue

创建todolist的主体:

依次是MyHeader、MyList、MyFooter

一些准备:

html及css样式均已上传到网盘

链接:https://pan.baidu.com/s/1ZoIEjkAfBwy_27rkqneR_w

提取码:js8p

导入相关组件

import MyHeader from './components/MyHeader'
  import MyList from './components/MyList'
  import MyFooter from './components/MyFooter.vue'
components:{MyHeader,MyList,MyFooter}

完整代码:

<template>
  <div id="root">
    <div class="todo-container">
      <div class="todo-wrap">
        <MyHeader :addTodo="addTodo"/>
        <MyList :todos="todos" :checkTodo="checkTodo" :deleteTodo="deleteTodo"/>
        <MyFooter :todos="todos" :checkAllTodo="checkAllTodo" :clearAllTodo="clearAllTodo"/>
      </div>
    </div>
  </div>
</template>
<script>
  import MyHeader from './components/MyHeader'
  import MyList from './components/MyList'
  import MyFooter from './components/MyFooter.vue'
  export default {
    name:'App',
    components:{MyHeader,MyList,MyFooter},
    data() {
      return {
        //由于todos是MyHeader组件和MyFooter组件都在使用,所以放在App中(状态提升)
        todos:JSON.parse(localStorage.getItem('todos')) || []
      }
    },
    methods: {
      //添加一个todo
      addTodo(todoObj){
        this.todos.unshift(todoObj)
      },
      //勾选or取消勾选一个todo
      checkTodo(id){
        this.todos.forEach((todo)=>{
          if(todo.id === id) todo.done = !todo.done
        })
      },
      //删除一个todo
      deleteTodo(id){
        this.todos = this.todos.filter( todo => todo.id !== id )
      },
      //全选or取消全选
      checkAllTodo(done){
        this.todos.forEach((todo)=>{
          todo.done = done
        })
      },
      //清除所有已经完成的todo
      clearAllTodo(){
        this.todos = this.todos.filter((todo)=>{
          return !todo.done
        })
      }
    },
    watch: {
      todos:{
        deep:true,
        handler(value){
          localStorage.setItem('todos',JSON.stringify(value))
        }
      }
    }
  }
</script>
<style>
  /*base*/
  body {
    background: #fff;
  }
  .btn {
    display: inline-block;
    padding: 4px 12px;
    margin-bottom: 0;
    font-size: 14px;
    line-height: 20px;
    text-align: center;
    vertical-align: middle;
    cursor: pointer;
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
    border-radius: 4px;
  }
  .btn-danger {
    color: #fff;
    background-color: #da4f49;
    border: 1px solid #bd362f;
  }
  .btn-danger:hover {
    color: #fff;
    background-color: #bd362f;
  }
  .btn:focus {
    outline: none;
  }
  .todo-container {
    width: 600px;
    margin: 0 auto;
  }
  .todo-container .todo-wrap {
    padding: 10px;
    border: 1px solid #ddd;
    border-radius: 5px;
  }
</style>

MyHeader.vue

接收从App传递过来的addTodo

收集用户输入的title

校验数据

将用户的输入包装成一个todo对象

清空输入

<input type="text" placeholder="请输入你的任务名称,按回车键确认" v-model="title" @keyup.enter="add"/>
import {nanoid} from 'nanoid'
  export default {
    name:'MyHeader',
    props:['addTodo'],
    data() {
      return {
        title:''
      }
    },
    methods: {
      add(){
        if(!this.title.trim()) return alert('输入不能为空')
        const todoObj = {id:nanoid(),title:this.title,done:false}
        this.addTodo(todoObj)
        this.title = ''
      }
    },
  }

完整代码:

<template>
  <div class="todo-header">
    <input type="text" placeholder="请输入你的任务名称,按回车键确认" v-model="title" @keyup.enter="add"/>
  </div>
</template>
<script>
  import {nanoid} from 'nanoid'
  export default {
    name:'MyHeader',
    //接收从App传递过来的addTodo
    props:['addTodo'],
    data() {
      return {
        //收集用户输入的title
        title:''
      }
    },
    methods: {
      add(){
        //校验数据
        if(!this.title.trim()) return alert('输入不能为空')
        //将用户的输入包装成一个todo对象
        const todoObj = {id:nanoid(),title:this.title,done:false}
        //通知App组件去添加一个todo对象
        this.addTodo(todoObj)
        //清空输入
        this.title = ''
      }
    },
  }
</script>
<style scoped>
  /*header*/
  .todo-header input {
    width: 560px;
    height: 28px;
    font-size: 14px;
    border: 1px solid #ccc;
    border-radius: 4px;
    padding: 4px 7px;
  }
  .todo-header input:focus {
    outline: none;
    border-color: rgba(82, 168, 236, 0.8);
    box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
  }
</style>

MyList.vue

声明接收App传递过来的数据,其中todos是自己用的,checkTodo和deleteTodo是给子组件MyItem用的

<MyItem
      v-for="todoObj in todos"
      :key="todoObj.id"
      :todo="todoObj"
      :checkTodo="checkTodo"
      :deleteTodo="deleteTodo"
    />
import MyItem from "./MyItem";
export default {
  name: "MyList",
  components: { MyItem },
  props: ["todos", "checkTodo", "deleteTodo"],
};

完整代码:

<template>
  <ul class="todo-main">
    <MyItem
      v-for="todoObj in todos"
      :key="todoObj.id"
      :todo="todoObj"
      :checkTodo="checkTodo"
      :deleteTodo="deleteTodo"
    />
  </ul>
</template>
<script>
import MyItem from "./MyItem";
export default {
  name: "MyList",
  components: { MyItem },
  //声明接收App传递过来的数据,其中todos是自己用的,checkTodo和deleteTodo是给子组件MyItem用的
  props: ["todos", "checkTodo", "deleteTodo"],
};
</script>
<style scoped>
/*main*/
.todo-main {
  margin-left: 0px;
  border: 1px solid #ddd;
  border-radius: 2px;
  padding: 0px;
}
.todo-empty {
  height: 40px;
  line-height: 40px;
  border: 1px solid #ddd;
  border-radius: 2px;
  padding-left: 5px;
  margin-top: 10px;
}
</style>

MyFooter.vue

<template>
  <div class="todo-footer" v-show="total">
    <label>
      <!-- <input type="checkbox" :checked="isAll" @change="checkAll"/> -->
      <input type="checkbox" v-model="isAll" />
    </label>
    <span>
      <span>已完成{{ doneTotal }}</span> / 全部{{ total }}
    </span>
    <button class="btn btn-danger" @click="clearAll">清除已完成任务</button>
  </div>
</template>
<script>
  export default {
    name:'MyFooter',
    props:['todos','checkAllTodo','clearAllTodo'],
    computed: {
      //总数
      total(){
        return this.todos.length
      },
      //已完成数
      doneTotal(){
        //此处使用reduce方法做条件统计
        // const x =this.todos.reduce((pre.current) =>{
        //  console.log('@',pre)
        //  return pre + 1
        // },0)
        //简写
        return this.todos.reduce((pre,todo)=> pre + (todo.done ? 1 : 0) ,0)
      },
      //控制全选框
      isAll:{
        //全选框是否勾选
        get(){
          return this.doneTotal === this.total && this.total > 0
        },
        //isAll被修改时set被调用
        set(value){
          this.checkAllTodo(value)
        }
      }
    },
    methods: {
      /* checkAll(e){
        this.checkAllTodo(e.target.checked)
      } */
      //清空所有已完成
      clearAll(){
        this.clearAllTodo()
      }
    },
  }
</script>
<style scoped>
/*footer*/
.todo-footer {
  height: 40px;
  line-height: 40px;
  padding-left: 6px;
  margin-top: 5px;
}
.todo-footer label {
  display: inline-block;
  margin-right: 20px;
  cursor: pointer;
}
.todo-footer label input {
  position: relative;
  top: -1px;
  vertical-align: middle;
  margin-right: 5px;
}
.todo-footer button {
  float: right;
  margin-top: 5px;
}
</style>

MyItem.vue

勾选or取消勾选

通知App组件将对应的todo对象的done值取反

通知App组件将对应的todo对象删除

<template>
  <li>
    <label>
      <!-- <input type="checkbox" :checked="todo.done" @change="handleCheck(todo.id)"/> -->
      <!-- 如下代码也能实现功能,但是不太推荐,因为有点违反原则,因为修改了props -->
      <!-- <input type="checkbox" v-model="todo.done"/> -->
      <input type="checkbox" v-model="todo.done"/>
      <span>{{todo.title}}</span>
    </label>
    <button class="btn btn-danger" @click="handleDelete(todo.id)">删除</button>
  </li>
</template>
<script>
  export default {
    name:'MyItem',
    //声明接收todo、checkTodo、deleteTodo
    props:['todo','checkTodo','deleteTodo'],
    methods: {
      handleCheck(id){
        this.checkTodo(id)
      },
      handleDelete(id){
        if(confirm('确定删除吗?')){
          this.deleteTodo(id)
        }
      }
    },
  }
</script>
<style scoped>
  /*item*/
  li {
    list-style: none;
    height: 36px;
    line-height: 36px;
    padding: 0 5px;
    border-bottom: 1px solid #ddd;
  }
  li label {
    float: left;
    cursor: pointer;
  }
  li label li input {
    vertical-align: middle;
    margin-right: 6px;
    position: relative;
    top: -1px;
  }
  li button {
    float: right;
    display: none;
    margin-top: 3px;
  }
  li:before {
    content: initial;
  }
  li:last-child {
    border-bottom: none;
  }
  li:hover{
    background-color: #ddd;
  }
  li:hover button{
    display: block;
  }
</style>

运行效果:

❤️往期精彩回顾❤️:

[前端学习]大数据全栈工程师之一文快速上手vue3

[前端学习]一文搞定Vue指令

[前端学习]文件组件专题

总结

到这里恭喜你把Vue入门的第一个案例做完了,如果你还有什么不懂,可随时问我,私信我,如果C站没有回复你,可在公众号后台加我好友,备注:C站来的。希望以上能够帮到刚学习Vue的你!


目录
相关文章
|
22天前
|
JavaScript 前端开发 程序员
前端学习笔记——node.js
前端学习笔记——node.js
34 0
|
9天前
|
存储 JavaScript 前端开发
vue3的脚手架模板你真的了解吗?里面有很多值得我们学习的地方!
【10月更文挑战第21天】 vue3的脚手架模板你真的了解吗?里面有很多值得我们学习的地方!
vue3的脚手架模板你真的了解吗?里面有很多值得我们学习的地方!
|
23天前
|
JavaScript 前端开发 API
Vue.js:现代前端开发的强大框架
【10月更文挑战第11天】Vue.js:现代前端开发的强大框架
63 41
|
2天前
|
前端开发 开发者 C++
独家揭秘:前端大牛们如何高效学习新技术,保持竞争力!
【10月更文挑战第31天】前端技术飞速发展,如何高效学习新技术成为关键。本文通过对比普通开发者与大牛们的策略,揭示了高效学习的秘诀:明确目标、主动探索、系统资源、实践应用和持续学习。通过这些方法,大牛们能更好地掌握新技术,保持竞争力。示例代码展示了如何通过实践加深理解。
14 4
|
9天前
|
前端开发 JavaScript 开发者
React与Vue:前端框架的巅峰对决与选择策略
【10月更文挑战第23天】React与Vue:前端框架的巅峰对决与选择策略
|
9天前
|
前端开发 JavaScript 数据管理
React与Vue:两大前端框架的较量与选择策略
【10月更文挑战第23天】React与Vue:两大前端框架的较量与选择策略
|
14天前
|
JavaScript 前端开发 算法
前端优化之超大数组更新:深入分析Vue/React/Svelte的更新渲染策略
本文对比了 Vue、React 和 Svelte 在数组渲染方面的实现方式和优缺点,探讨了它们与直接操作 DOM 的差异及 Web Components 的实现方式。Vue 通过响应式系统自动管理数据变化,React 利用虚拟 DOM 和 `diffing` 算法优化更新,Svelte 通过编译时优化提升性能。文章还介绍了数组更新的优化策略,如使用 `key`、分片渲染、虚拟滚动等,帮助开发者在处理大型数组时提升性能。总结指出,选择合适的框架应根据项目复杂度和性能需求来决定。
|
21天前
|
前端开发 JavaScript 安全
在vue前端开发中基于refreshToken和axios拦截器实现token的无感刷新
在vue前端开发中基于refreshToken和axios拦截器实现token的无感刷新
55 4
|
21天前
|
JavaScript
《进阶篇第9章》学习vuex知识点后练习:求和案例_纯vue版代码
《进阶篇第9章》学习vuex知识点后练习:求和案例_纯vue版代码
12 1
|
25天前
|
JavaScript 前端开发 API
Vue.js:打造高效前端应用的最佳选择
【10月更文挑战第9天】Vue.js:打造高效前端应用的最佳选择
17 2