vue專案開發-3

大勇若怯任卷舒發表於2020-11-24

專案實戰

篩選

  • 有三種篩選:
    • 全部:顯示全部 todo
    • 進行中:顯示未完成的 todo
    • 已完成:顯示已完成的 todo
  • 使用者是想要進行中的 todo 還是已完成的todo?
    • 可以發現使用者會點選相應的按鈕來表明他的意圖
    • 如果在使用者點選按鈕時把他的意圖傳遞給 Vue 物件
  • 給 data 增加一個屬性 intention,來記錄使用者的意圖
  • 定義三種意圖:
    • all:想檢視全部 todo
    • ongoing:想檢視未完成的 todo
    • completed:想檢視已完成的 todo
  • 需要根據使用者的意圖從 todos 中篩選 todo
    • 從 Vue 已繫結的資料中計算新的結果是計算屬性的典型應用場景,所以加一個 filteredTodos 計算屬性
    • 會發現未完成的 todo 分別在 filteredTodos 和 leftTodosCount 兩個計算屬性中被計算了兩次
    • n 根據使用者意圖返回的結果:filteredTodos
  • 使用者點選按鈕的意圖需要傳給 Vue 物件
    • 設定讓使用者的意圖在點選按鈕時傳給 Vue
    • 給各個篩選按鈕繫結一個 click 事件
      • 之前在迴圈顯示 todo 列表時使用的是 this.todos 的資料
      • 把迴圈的內容改為 filteredTodos
  • 迴圈過濾結果
<ul>
    <li v-for='todo in filteredTodos' :key='todo.id'>
  • 繫結事件
<span>篩選:
    <input type="button" class="selected" value="全部"
           @click="intention='all'">
    <input type="button" value="進行中" 
           @click="intention='ongoing'">
    <input type="button" value="已完成"
           @click="intention='completed'">
  • vue物件增加data屬性和計算屬性
			data: function () {
...
					intention: 'all' 
...
			computed: {
...
					leftTodos(){
						return this.todos.filter(todo => !todo.completed)
					},
					leftTodoCount(){
						return this.leftTodos.length
					},
					filteredTodos(){
						if(this.intention == 'ongoing'){
							return this.leftTodos
						}else if (this.intention == 'completed'){
							return this.todos.filter(todo => todo.completed)
						}else { //返回所有的
							return this.todos
						}
					}

功能10:樣式繫結

  • 使用v-bind:class繫結
<span>篩選:
    <input type="button" class="selected" value="全部"
           @click="intention='all'"
           v-bind:class="{selected: intention==='all'}">
    <input type="button" value="進行中" 
           @click="intention='ongoing'"
           v-bind:class="{selected: intention==='ongoing'}">
    <input type="button" value="已完成"
           @click="intention='completed'"
           v-bind:class="{selected: intention==='completed'}">

批量清除

  • 只要把篩選出來的 todo 從 todos 列表刪除就可以了
    • 先為按鈕繫結事件
    • 然後實現相應的繫結方法
  • 繫結事件
<input type="button" value="清除已完成"
       @click="clearCompleted">
<input type="button" value="清除全部"
       @click="clearAll">
  • 實現方法methods
clearCompleted(){
    this.todos = this.todos.filter(todo => !todo.completed)
},
clearAll(){
    this.todos = []
}

功能11:動態顯示按鈕

  • 繫結v-if條件刷選
			  <span>篩選:
...
			    <input v-if="leftTodoCount" type="button" value="進行中" 
						@click="intention='ongoing'"
						v-bind:class="{selected: intention==='ongoing'}">
			    <input v-if="completedTodoCount" type="button" value="已完成"
						@click="intention='completed'"
						v-bind:class="{selected: intention==='completed'}">
			    <input v-if="completedTodoCount" type="button" value="清除已完成"
						@click="clearCompleted">
  • 增加和修改計算屬性
computed: {
    ...
    completedTodos(){
        return this.todos.filter(todo => todo.completed)
    },
        completedTodoCount(){
            return this.completedTodos.length
        },

功能12:使用者體驗

  • 增加span的v-if
<div>
    <span v-if="leftTodoCount">剩餘 {{ leftTodoCount }} 項未完成 ---</span>
    <span v-else-if="completedTodoCount">全部完成,你真是太優秀了</span>
    <span v-else>新增我的第一個TODO</span>

  • 增加清除確認
clearCompleted(){
    if(!confirm('確定清除已完成的待辦事項?')){
        return
    }
    this.todos = this.todos.filter(todo => !todo.completed)
},
    clearAll(){
        if(!confirm('確定清除全部的待辦事項?')){
            return
        }
        this.todos = []
    }

功能13:回收站

  • 新增一個回收站data物件:recycleBin
			data: function () {
				return {
...
					recycleBin:[], //回收站

  • 新增刪除標記:removed
			            addTodo: function () {
...
			                this.todos.push({id: id++, title: this.newTodoTitle,
											completed:false, removed:false});
...
			            },

  • 修改removedTodo,clearCompleted,clearAll,將刪除資料移動到回收站
removeTodo(todo){
    let pos = this.todos.indexOf(todo);
    let removedTodo = this.todos.splice(pos, 1)[0];
    removedTodo.removed = true;
    this.recycleBin.unshift(removedTodo)
},
    clearCompleted(){
        if(!confirm('確定清除已完成的待辦事項?')){
            return
        }
        this.completedTodos.map(todo => todo.removed = true)
        this.recycleBin.unshift(...this.completedTodos)
        this.todos = this.leftTodos
    },
        clearAll(){
            if(!confirm('確定清除全部的待辦事項?')){
                return
            }
            this.todos.map(todo => todo.removed = true)
            this.recycleBin.unshift(...this.todos)
            this.todos = []
        }

  • DOM增加回收站按鈕,繫結事件
<input v-if="recycleBin.length" type="button" value="回收站"
       @click="intention='removed'">
...

					filteredTodos(){
...
						}else if (this.intention == 'removed'){
							return this.recycleBin
...
					}

從回收站還原

  • 還原按鈕
		        <li v-for='todo in filteredTodos' :key='todo.id'>
...
				  <input v-if="todo.removed" type="button" value="還原" 
						@click="restoreTodo(todo)"/>
				  <input v-else="todo.removed" type="button" value="刪除" 
						@click="removeTodo(todo)"/>

  • 新增方法
restoreTodo: function(todo){
    todo.removed = false
    this.todos.unshift(todo) //恢復
    pos = this.recycleBin.indexOf(todo)
    this.recycleBin.splice(pos, 1)
},

本地儲存

  • Vue 提供了一種更通用的方式來觀察和響應 Vue 例項上的資料變動:偵聽屬性
    在這裡插入圖片描述

  • 現在只要一重新整理瀏覽器,所有todo 都沒了,因為的資料儲存在記憶體中,頁面重新整理資料就會清除

  • 可以使用瀏覽器的 LocalStorage 來實現資料的持續性儲存

    • 這樣一來只要使用者不刪除瀏覽器快取,todo 會一直在
    • 當然瀏覽器清除快取後資料也沒有了
    • 為了更加持久化儲存,可以使用資料庫,這裡只是用 LocalStorage
  • 首先來定義一個物件,用於 LocalStorage 儲存和獲取 todo 的相關操作

    • STORAGE_KEY 用來區分儲存到 LocalStorage 的內容,因為 LocalStorage 中可能儲存其它應用的資料,使用這個 key 用於區分
    • todoStorage 是一個 JavaScript 的物件,它的屬性是兩個方法
      • save 方法:把 todos 轉為 JSON 格式,然後將序列化的資料存入對應 key 為 STORAGE_KEY 的本地儲存中
      • fetch 方法:從對應 STORAGE_KEY 的本地儲存將之前存入的 todo 資料取出並反序列化
  • 為 todoStorage 物件繫結一個 uid 屬性,作用是後續新增 todo 時,用於確定新新增todo 的 id

    • 注意這裡程式碼中的 localStorage 就代表了本地儲存物件,在支援 HTLM5 的瀏覽器中會存在這個物件,直接引用即可
  • 每當使用者開啟頁面時,因為去 LocalStorage fetch 一下儲存的資料

  • 當新增 todo 時,由於可能已經存在從本地取出的 todo 資料,新的 todo id 不能是從 0 開始了,而應該從已有 todoStorage.uid 開始

    • 一旦新增了新的 todo,應該及時將新的 todo 存到本地,防止使用者不小心關閉頁面而導致資料丟失
    • 可以使用 Vue 的 watch 來監聽使用者新增 todo 的事件,即監視 this.todos 的變化,一旦改變,立即修改本地儲存的 todos 的資料
  • 定義localstorage儲存和獲取todo

	<script>
		let id = 0; // 用於 id 生成
		var STORAGE_KEY = "dylan-vue2-todo"
		var todoStorage = {
			fetch(){ //讀
				var todos = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]')
				todos.forEach(function(todo, index){
					todo.id = index
				});
				todoStorage.uid = todos.length
				return todos
			},
			save(todos){ //寫
				localStorage.setItem(STORAGE_KEY,JSON.stringify(todos))
			}
		};

  • 修改data
			data: function () {
				return {
					todos: todoStorage.fetch(),
                    ...

  • 修改addTodo
			            addTodo: function () {
...
			                this.todos.push({id: todoStorage.uid++, title: this.newTodoTitle,
...

  • 增加監聽
watch:{
    todos:{
        handler: function(todos){
            todoStorage.save(todos)
        },
            deep: true
    }
},

相關文章