實現一個TodoList - Vue2 Tutorials (二)

在瞭解了 Vue 的一些基本概念以後,就能夠寫一個最簡單的小項目了 --- TodoList。麻雀雖小,五張俱全。雖然是一個小 demo,但也涉及到了組件化、雙向綁定、自定義事件的觸發與監聽、計算屬性等概念。接下來從這個小項目中,對這些基本概念進行實踐,從而加深理解。html

本文的全部代碼在 https://github.com/nodejh/vue2-tutorials/tree/master/02.TodoListvue

最終實現效果以下:node

02.TodoLost-3.png

接下來就一一實現。webpack

初始化項目

一樣使用 vue-cli 初始化項目,直接回車就行了。git

$ vue init webpack todoListDemo
$ cd todoListDemo
$ npm install
$ npm run dev

啓動以後,瀏覽器就會自動現默認的頁面。github

在進行編碼以前,首先要考慮組件怎麼設計。在本文中,組件結構以下。web

+-----------------------+
|                       |
|  +-----------------+  |
|  |    Todo Add     |  |
|  +-----------------+  |
|  +-----------------+  |
|  |                 |  |
|  |   Todo List     |  |
|  |+---------------+|  |
|  ||   Todo Item   ||  |
|  |+---------------+|  |
|  |+---------------+|  |
|  ||   Todo Item   ||  |
|  |+---------------+|  |
|  |+---------------+|  |
|  ||   Todo Item   ||  |
|  |+---------------+|  |
|  |                 |  |
|  +-----------------+  |
|                       |
+-----------------------+

其中主要包括兩個大的組件vue-router

  • TodoAdd 添加 Todo 的一個輸入框vue-cli

  • TodoList Todo 列表,裏面有每個 Todo Itemshell

添加 TodoList 組件

src/components 目錄下新建一個名爲 TodoList.vue 的文件,並添加以下代碼:

<template>
  <div id="todoList">
    <h1>Todo List</h1>
    <ul class="todos">
      <li v-for="todo, index in todos" class="todo">
        <input
          type="checkbox"
          name=""
          value=""
          :checked="todo.isCompleted"
        >
        <span
          :class="todo.isCompleted ? 'completed' : ''"
          @
        >
          <em>{{ index + 1 }}.</em>{{ todo.text }}
        </span>
      </li>
    </ul>
  </div>
</template>
<script>
export default {
  name: 'TodoList',
  data: () => ({
    todos: [{
      text: '吃飯',
      isCompleted: false
    }, {
      text: '睡覺',
      isCompleted: false
    }]
  })
}
</script>
<style scoped>
#todoList {
  margin: 0 auto;
  max-width: 350px;
}
.todos li {
  list-style: none;
}
.todo {
  text-align: left;
  cursor: pointer;
}
.completed {
  text-decoration: line-through;
}
</style>

TodoList 中,使用 todos 數組來保存全部的 todo list。其中每個 todo 都是對象,對象裏面有兩個屬性,分別是 todo 的內容,和 todo 是否完成的標誌。默認給數組添加了兩個 todo,主要用於演示。

src/components/Hello.vue 在本項目中沒什麼用,能夠隨意刪除。

而後修改 src/router/index.js

import Vue from 'vue'
import Router from 'vue-router'
import TodoList from '@/components/TodoList'

Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/',
      name: 'todoList',
      component: TodoList
    }
  ]
})

修改完以後,vue 會自動從新編譯並刷新頁面,這時瀏覽器的頁面以下:

02.TodoLost-1.png

添加完成 Todo 的方法

在該 demo 中,當點擊 todo item 或者前面的複選框的時候,就完成 todo。因此如今須要添加完成 todo 的方法,並設置 todo item 的點擊事件。

像下面這樣修改 src/components/TodoList.vue 中的 template 部分:

<input
  type="checkbox"
  name=""
  value=""
  :checked="todo.isCompleted"
  @click="completed(index)"
>
<span
  :class="todo.isCompleted ? 'completed' : ''"
  @click="completed(index)"
>
  <em>{{ index }}.</em>{{ todo.text }}
</span>

而後在組件裏面添加對應的 completed 方法:

<script>
export default {
  // 其餘現有代碼
  name: 'TodoList',
  methods: {
    completed(index) {
      this.todos[index].isCompleted = !this.todos[index].isCompleted
    }
  }
}
</script>

當點擊 check boxspan 的時候,就調用 completed 方法並傳入被點擊的 todo item 的索引。在 completed 方法裏面,更新數據對象 data 裏面對應的 todo item 的 isCompleted 屬性。這樣就實現了完成 todo 和取消完成 todo 的功能。點擊以後如圖:

02.TodoLost-2.png

TodoAdd 組件

接下來就須要完成添加新的 todo 的功能了。

新建一個文件 src/components/TodoAdd.vue,添加以下代碼:

<template>
  <div id="addTodo">
    <input
      type="text"
      name=""
      class="input"
      value=""
      v-model="todo"
      @keyup.enter="addTodo"
    >
    <button
      type="button"
      name="button"
      @click="addTodo"
    >
        添加
    </button>
  </div>
</template>
<script>
export default {
  name: 'addTodo',
  data: () => ({
    todo: ''
  }),
  methods: {
    addTodo () {
      if (this.todo) {
        this.$emit('add', this.todo)
        this.todo = ''
      } else {
        alert('內容不能爲空')
      }
    }
  }
}
</script>
<style scoped>
.input {
  min-width: 200px;
}
</style>

首先在組件的數據對象 data 裏面有一個 todo 屬性,用來存儲用戶輸入的內容。而後在 templateinput 輸入框裏,使用 v-model 實現雙向綁定。

當用戶按下回車(@keyup.enter="addTodo",詳見 鍵值修飾符)或者點擊添加按鈕(@click="addTodo")的時候,就調用 methods 裏面的 addTodo 方法。

addTodo 方法經過 vm.$emit 觸發了一個 add 事件,並將用戶輸入的內容(即 this.todo)做爲參數傳遞。事件觸發以後,將輸入框中的內容清空。

接下來就須要監聽 add 事件了。監聽事件須要在使用組件的模板裏面,經過 v-on 來實現。詳見 使用-v-on-綁定自定義事件

完成添加 Todo 功能

src/components/TodoList.vue 中使用 AddTodo 這個子組件:

<h1>Todo List</h1>
<!-- 調用子組件,並使用 v-on 監聽 add 方法 -->
<!-- 當 add 事件觸發時,就調用當前組件 addTodo 這個方法 -->
<todo-add v-on:add="addTodo"></todo-add>
<ul class="todos">
<!-- // 調用子組件 -->

<script>
  // 引入子組件
  import TodoAdd from './TodoAdd.vue'
  export default {
    name: 'TodoList',
    components: {
      TodoAdd
    },
    // ...
    methods: {
      // ...
      // 添加新的 todo
      addTodo() {
        this.todos.push({
          text: todo,
          isCompleted: false
        })
      }
    }
  }
</script>

到此,添加 todo 和完成 todo 功能就實現了。

Todo 統計

接下來還能夠作點別的事情,好比顯示總共的 todo 數目,以及完成和未完成的數目。

要實現此功能,方法有不少種。最簡單的一種是直接在模板中加入 JS 表達式,來顯示總共的數目,好比:

<p>總共有 <strong>{{ this.todos.lengt }}</strong> 個待辦事項。</p>

對於簡單的邏輯能夠很方便用表達式寫出來,但若是是比較複雜的邏輯,好比統計未完成數目(固然這個也能夠用一個表達式搞定),可能一個表達式看起來就不太清晰。這個時候就能夠用計算屬性

修改 src/components/TodoList.vue

<!-- // ... -->
<ul class="todos">
<!-- // ... -->
</ul>
<div>
  <p v-show="todos.length === 0">
    恭喜!全部的事情都已完成!
  </p>
  <p v-show="todos.length !== 0">
    共 <strong>{{ todos.length }}</strong> 個待辦事項。{{ completedCounts }} 個已完成,{{ notCompletedCounts }} 個未完成。
  </p>
</div>


<script>
// ...
export default {
  name: 'TodoList',
  // ...
  computed: {
    completedCounts () {
      return this.todos.filter(item => item.isCompleted).length
    },
    notCompletedCounts () {
      return this.todos.filter(item => !item.isCompleted).length
    }
  }
}
</script>

上述代碼中經過 completedCountsnotCompletedCounts 兩個計算屬性,來計算出已完成和未完成的 todo。雖然這兩個表達式能夠直接放在模板中,但表達式比較複雜,看起來也不是很清晰,因此不少時候就能夠用計算屬性來計算出一個最終值,而後在模板中使用。

總結

到此,基於 Vue 的 Todo List 就完成了。在該項目中,對組件化、雙向綁定、自定義事件的觸發與監聽、計算屬性等概念進行了實踐。固然,最重要的不是完成這個 Todo List 的代碼,而是從實現功能的過程當中觸類旁通,經過簡單的 demo 實現,去思考如何用 vue 開發一個更大更完整的項目。

相關文章
相關標籤/搜索