Hugo 雖然常搭配 Petite-vue,但只能使用 reactive()
,也沒 computed()
可用,若搭配 Vue 3 就能使用 ref()
與 computed()
。
Version
Vue 3.4
Hugo X Vue 3
<!doctype html>
<html>
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
{{ $title := "Hugo X Vue 3 Lab" }}
<title>{{ $title }}</title>
</head>
<body id="app">
<div>
<input type="text" v-model="newTodo" />
<button @click="onAdd">Add</button>
</div>
<div>
<ul>
<li v-for="(item, i) in filteredTodos" :key="i">
<input type="checkbox" v-model="item.isCompleted" />
<span v-if="!item.isEdit">[[ item.todo ]]</span>
<span v-else><input type="text" v-model="item.todo" /></span>
<span v-if="!item.isEdit"><button @click="item.isEdit = true">edit</button></span>
<span v-else><button @click="item.isEdit = false">save</button></span>
<button @click="onDelete(item)">delete</button>
</li>
</ul>
</div>
<div>
<span> [[ active.length ]] items left</span>
<button @click="onAll">All</button>
<button @click="onActive">Active</button>
<button @click="onCompleted">Completed</button>
<button v-if="completed.length" @click="onClearCompleted">Clear Completed</button>
</div>
</body>
</html>
<script type="module">
import { createApp, ref, computed } from './js/vue.esm-browser.prod.js'
let newTodo = ref('')
let todos = ref([])
let filter = ref(() => true)
let active = computed(() => todos.value.filter(todo => !todo.isCompleted))
let completed = computed(() => todos.value.filter(todo => todo.isCompleted))
let filteredTodos = computed(() => todos.value.filter(filter.value))
let onAdd = () => {
if (!newTodo.value) return
todos.value = [
...todos.value,
{
id: Symbol(),
isCompleted: false,
isEdit: false,
todo: newTodo.value
}
]
newTodo.value = ''
}
let onDelete = item => todos.value = todos.value.filter(todo => todo.id !== item.id)
let onAll = () => filter.value = () => true
let onActive = () => filter.value = todo => !todo.isCompleted
let onCompleted = () => filter.value = todo => todo.isCompleted
let onClearCompleted = () => todos.value = todos.value.filter(todo => !todo.isCompleted)
createApp({
delimiters: ['[[', ']]'],
setup: () => ({
newTodo,
todos,
filter,
active,
completed,
filteredTodos,
onAdd,
onDelete,
onAll,
onActive,
onCompleted,
onClearCompleted
})
}).mount('#app')
</script>
Line 11
<input type="text" v-model="newTodo" />
- v-model:將欲輸入的新 todo 以
雙向綁定
到newTodo
Line 12
<button @click="onAdd">Add</button>
- 將 Add 的
click
event 指定到onAdd()
Line 15
<ul>
<li v-for="(item, i) in filteredTodos" :key="i">
<span>[[ item.todo ]]</span>
</li>
</ul>
v-for
:列舉filteredTodos()
Line 17
<input type="checkbox" v-model="item.isCompleted" />
- v-model:將 checkbox 以
雙向綁定
到item.isCompleted
Line 18
<span v-if="!item.isEdit">[[ item.todo ]]</span>
<span v-else><input type="text" v-model="item.todo" /></span>
- v-if:若不是
編輯模式
,則直接顯示 - v-else:否則顯示
<input>
Line 20
<span v-if="!item.isEdit"><button @click="item.isEdit = true">edit</button></span>
<span v-else><button @click="item.isEdit = false">save</button></span>
- v-if:若不是
編輯模式
,則顯示edit
button - v-else:否則顯示
save
button
Line 22
<button @click="onDelete(item)">delete</button>
- 將 delete 的
click
event 指定到onDelete()
Line 27
<span> [[ active.length ]] items left</span>
- 顯示目前尚未完成的 todo 筆數
Line 28
<button @click="onAll">All</button>
- 顯示所有 todo,將 click event 指定到
onAll()
Line 29
<button @click="onActive">Active</button>
- 顯示尚未完成 todo,將 click event 指定到
onActive()
Line 30
<button @click="onCompleted">Completed</button>
- 顯示已完成 todo,將 click event 指定到
onCompleted()
Line 31
<button v-if="completed.length" @click="onClearCompleted">Clear Completed</button>
- v-if:若有已完成 todo,則顯示
Clear Completed
button
Line 39
let newTodo = ref('')
let todos = ref([])
let filter = ref(() => true)
- 使用
ref()
宣告 state - newTodo:儲存新輸入的 todo
- todos:儲存所有 todo
- filter:儲存
All
、Active
與 Completed
所需要的 filter function
Line 43
let active = computed(() => todos.value.filter(todo => !todo.isCompleted))
- active Computed:當
todo
改變時,active
Computed 會 reactive 跟著改變,只顯示所有未完成
todo
Line 44
let completed = computed(() => todos.value.filter(todo => todo.isCompleted))
- completed Computed:當
todo
改變時,completed
Computed 會 reactive 跟著改變,只顯示所有已完成
todo
Line 45
let filteredTodos = computed(() => todos.value.filter(filter.value))
- filteredTodos Computed:當
todo
改變時,filteredTodos
Computed 會 reactive 跟著改變,只顯示所有目前 filter function 所指定的 todo
Line 47
let onAdd = () => {
if (!newTodo.value) return
todos.value = [
...todos.value,
{
id: Symbol(),
isCompleted: false,
isEdit: false,
todo: newTodo.value
}
]
newTodo.value = ''
}
- 使用
push
新增todo
- id 使用
Symbol()
實現 UUID
Line 62
let onDelete = item => todos.value = todos.value.filter(todo => todo.id !== item.id)
- 使用
filter()
取代splice()
刪除 Array 中的 element
Line 64
let onAll = () => filter.value = () => true
- 顯示所有 todo,直接提供 filter function
Line 66
let onActive = () => filter.value = todo => !todo.isCompleted
- 顯示尚未完成 todo,直接提供 filter function
Line 68
let onCompleted = () => filter.value = todo => todo.isCompleted
- 顯示已完成 todo,直接提供 filter function
Line 70
let onClearCompleted = () => todos.value = todos.value.filter(todo => !todo.isCompleted)
- 使用
filter()
取代splice()
刪除 Array 中的 element
Line 72
createApp({
delimiters: ['[[', ']]'],
setup: () => ({
newTodo,
todos,
filter,
active,
completed,
filteredTodos,
onAdd,
onDelete,
onAll,
onActive,
onCompleted,
onClearCompleted
})
}).mount('#app')
- 使用
setup()
整合所有 state 與 function - $delimiters:為了避開 Go template 的 delimiter
Conclusion
- 在 Vue 3 可直接使用
ref()
與computed()
,DX 體驗較 Petite-vue 佳 - 由於沒有 Script Setup,只能在
setup()
中整合所有 state 與 function