待办案例的实践学习(vue实现)上篇 | 青训营

93 阅读2分钟

青训营学习记录笔记

01 技术使用

todolist案例为前端案例中最为经典案例之一,几乎是前端学习一定会接触到的案例。目前已经有很多语言和框架来写的todolist案例。今天简单记录下使用vue建立todolist案例的学习过程。

本次TodoList待办案例编码使用到的技术有

  • vue2
  • vue-cli

02 实现一个todoList静态页面

  • 本次所创建的静态页面如下图:

image-20230815225627467.png

  • 先针对网页原型进行组件拆分,本次所拆分的组件可供参考:

image-20230815230445716.png

  • 分别对不同组件使用vue组件进行静态页面的实现:
  1. App组件:

    <template>
      <div id="root">
        <div class="todo-container">
          <div class="todo-wrap">
            <TodoHeader/>
            <TodoList/>
            <TodoFooter/>
          </div>
        </div>
      </div>
    </template>
    
    <script>
    import TodoHeader from './components/TodoHeader.vue';
    import TodoList from './components/TodoList.vue';
    import TodoFooter from './components/TodoFooter.vue';
    
    export default {
      name: 'App',
      components: {
        TodoHeader,
        TodoList,
        TodoFooter
      },
      data() {
          return {
            todos:[
              {id:'001',content:'学习',isDone:true},
              {id:'002',content:'睡觉',isDone:true},
              {id:'003',content:'玩原神',isDone:false},
            ]
          }
      }
    </script>
    
    <style>
      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-warning {
        color: #fff;
        background-color: rgb(255, 193, 7);
        border: 1px solid rgb(255, 180, 7);
      }
    
      .btn-warning:hover {
        color: #fff;
        background-color: rgb(255, 150, 7);
      }
    
      .btn-danger {
        color: #fff;
        background-color: #da4f49;
        border: 1px solid #da7f58;
      }
    
      .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>
    

    样式使用的原生CSS来写

  2. TodoHeader组件:

    <template>
      <div class="todo-header">
        <input type="text" placeholder="Please enter your todo name and press enter to confirm"/>
      </div>
    </template>
    
    <script>
      export default {
        name: 'TodoHeader',
      }
    </script>
    
    <style scoped>
      .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>
    
  3. TodoList组件:

    <template>
      <ul class="todo-main">
        <TodoItem 
          v-for="todoObj in todos"
          :key="todoObj.id" 
    	/>
      </ul>
    </template>
    
    <script>
      import TodoItem from './TodoItem.vue';
      export default {
        name: 'TodoList',
        components: { TodoItem },
      }
    </script>
    
    <style scoped>
      .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>
    

    在TodoList组件中,使用了v-for进行每个待办的遍历

  4. TodoItem组件:

    <template>
      <li>
        <label>
          <input type="checkbox" :checked="todo.isDone"/>
          <span>{{ todo.content }}</span>
        </label>
        <button class="btn btn-danger">删除</button>
      </li>
    </template>
    
    <script>
      export default {
        name: 'TodoItem',
      }
    </script>
    
    <style scoped>
      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: rgb(203, 234, 254);
      }
    
      li:hover button {
        display: block;
      }
    </style>
    

    在Item组件中,使用了checked="todo.isDone"进行复选框的初始设定

  5. TodoFooter组件:

    <template>
      <div class="todo-footer">
        <label>
          <input type="checkbox"/>
        </label>
        <span>
          <span>已完成</span> / 全部
        </span>
        <button class="btn btn-warning">清除已完成任务</button>
      </div>
    </template>
    
    <script>
      export default {
        name: 'TodoFooter',
      }
    </script>
    
    <style>
      .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>
    

03 实现TodoHeader组件功能

TodoHeader主要功能为一个复选框,首先要实现将用户的输入包装成一个todo对象,而对于在本地书写代码,没有后端服务器支撑的话,不能为每一个输入的id赋值,在此我们引用nanoid库来处理输入待办的ID

执行命令:npm install nanoid

为复选框添加键盘事件,本次所选择的是按下ente,r进行输入:@keyup.enter="add"

在vue的script部分添加一个名为addmethods

<script>
  import {nanoid} from 'nanoid'
  export default {
    name: 'TodoHeader',
    methods: {
      add(){
        if(!this.content.trim()) {
          this.content = ''
          return alert('输入内容不能为空!')
        }
        const todoObj = { 
          id:nanoid(), 
          content: this.content,
          isDone: false
        }
        this.addTodo(todoObj)
        //按enter后清空输入框的输入内容
        this.content = ''
      }
    }
  }
</script>

同时,考虑用户输入之后,再输入下一个待办的时候,希望的是复选框里内容为空:

在input框里输入v-model="content"

methods里加入data:

    data(){
      return {
        content:''
      }
    },

本案例未使用组件之间通信技术,对于增加一个todo的实现,是使用App组件的定义函数,再进行回调的方式:

App组件定义添加一个todo方法:

   addTodo(todoObj){
      this.todos.unshift(todoObj)
    },

然后进行组件传参(props),后同:<TodoHeader :addTodo="addTodo"/>

在TodoHeader组件中接受:props:['addTodo']