Todo List: Vue.js Example
07 Mar 2017利用 Vue.js 簡單實作一個 Todo List。
功能介紹
實作的功能有
- 新增一個 todo
- 修改一個 todo 的內容
- 變更一個 todo 的狀態:已完成或未完成
- 刪除一個 todo
- 切換檢視模式:看全部、已完成或未完成的 todo
- 在檢視按鈕上顯示目前全部、已完成和未完成的 todo 數量
Demo
原始碼在這裡。
環境設定
由於使用 gulp 轉換 less 為 css,因此若更改 less 檔案,要使用npm start
或gulp
來觀看更新後的畫面。
程式碼說明
- data:傳入初始資料,例如:todo list、預設檢視全部 todos。
- computed:動態計算需要即時更新的資料。例如:使用 computed 自動計算現在畫面要 render 符合怎樣條件的 todo list:全部、已完成或未完成的項目 (即 list)、自動計算全部 (allCount)、已完成 (completedCount) 和未完成 (incompleteCount) 項目的數量。這是 data-driven 的概念,data 中任何值被改變,computed 都會自動重新計算。
- method:自訂事件,例如:新增一個 todo (add)、刪除一個 todo (del)、切換檢視模式 (setFilter)。
- component:將可重用的部份定義為 component。這裡將 todo 切成可重用的元件,即
<todo-item>
。<todo-item>
會用到的 method 就寫在元件自定義的事件裡面。props 從父層會帶入要用的資料到子層 (props down),再用$emit
的方式將結果從子層傳回到父層 (events up)。
遇到的問題 / 解法
父子元件溝通時無法使用小駝峰 (camelCased) 命名自訂事件
父子元件溝通時,父層使用 props down 將資訊傳遞給子層,而子層透過 events up 的方式 (即$emit
) 將結果傳回父層。父層 method 使用小駝峰命名是無法運作的,這是由於部份 template 使用 HTML DOM 的緣故。
解法
- 父層 method 改為 lowercase。如範例所示,父層的 method
incrementTotal
改為incrementtotal
。 - 由於父層的樣版是使用 HTML DOM,若將父層這部份的程式碼也改為以 JavaScript 產生模版的方式,意即「字串模版 (String Template)」 的方式傳入就可以了。討論串請可參考 camelCased custom event name is not working #4044。
混合 JavaScript 物件和 Vue Instance 屬性一起操作
測試時發現刪不掉 todo,在加入新的 todo 後,完成、已完成和未完成的計數無法準確更新。看了好久才發現是因為混合 JavaScript 物件和 Vue Instance 屬性一起操作的關係 (如下圖 1 程式碼),也就是說,Vue.js 無法偵測物件新增的屬性,新加入的屬性並無 Reactivity 特性 - 在初始化時並無設定Object.defineProperty
和加入監聽。
圖 1
解法
使用Vue.set
加入新屬性,刪除則要使用Vue.delete
,確保之後的互動更新 (Reactivity)。
也就是,將圖 1 的程式碼改為圖 2 這樣…
圖 2
下圖 3 中,標示「1」的物件是使用Vue.set
產生的,而標示「2」的物件則是 JavaScript 物件,debug 時可以注意一下。
圖 3
v-if
和v-show
皆可控制顯示與否,兩者差異是?
- 使用上來說,差異在於是否有 else 的狀況。若有其他狀況,則使用
v-if
;否則v-if
和v-show
效果相同。 - 以 HTML render 上來說,
v-if
是有條件的 render 的,若條件判斷為 true 則 render;而v-show
則是無條件 render,但視條件以 inline style css 隱藏。