在 上一篇 中寫了一個 Todo 小樣,但 Vue.js 提供的便利不只有這些,還有元件化。下面將之前的 Todo 小樣重寫成可重複利用的元件,結果在 codepen.io 上可看到。
註冊元件
Vue.js 使用 Vue.component
註冊全域性元件,下面註冊元件 todo-items
。
Vue.component('todo-items', {
template: '#todo-items-template',
props: ['initialTodos'],
data() {
return {
todos: this.initialTodos ? [].concat(this.initialTodos) : [],
newTodo: { title: '', completed: false }
}
},
methods: {
add() {
if (! this.newTodo.title.trim()) { return ; }
this.todos.push({
title: this.newTodo.title,
completed: this.newTodo.completed
});
this.newTodo.title = '';
},
destroy(index) {
this.todos.splice(index, 1);
},
toggleDone(index) {
this.todos[index].completed = !this.todos[index].completed;
},
up(index) {
if (index - 1 < 0) { return ; }
var temp = this.todos[index];
this.todos[index] = this.todos[index-1];
this.todos[index-1] = temp;
this.todos = [].concat(this.todos);
},
down(index) {
if (index + 1 >= this.todos.length) { return ; }
var temp = this.todos[index];
this.todos[index] = this.todos[index+1];
this.todos[index+1] = temp;
this.todos = [].concat(this.todos);
}
}
});
元件中 data
資料是使用函式形式返回的物件,是為了每個元件例項都有獨一無二 data 物件。
寫模板程式碼
元件 todo-items
使用的 #todo-items-template
處的模板程式碼,就是之前的 Todos 的 HTML 程式碼,用 type="text/x-template
的 script 標籤包圍。
<script type="text/x-template" id="todo-items-template">
<div class="panel panel-default">
<div class="panel-heading text-center">
計劃要做的事情,共 {{ todos.length }} 件
</div>
<div class="panel-body">
<div class="list-groups">
<a class="list-group-item" v-bind:class="{ 'completed': todo.completed }" v-for="(todo, index) in todos">
{{ todo.title }}
<button class="btn btn-xs btn-danger pull-right" v-on:click='destroy(index)'title="刪除">✘</button>
<button class="btn btn-xs btn-info pull-right" v-on:click='down(index)'title="下移">↓</button>
<button class="btn btn-xs btn-info pull-right" v-on:click='up(index)'title="上移">↑</button>
<button class="btn btn-xs pull-right" v-on:click='toggleDone(index)' v-bind:class="[todo.completed ? 'btn-success': '']" v-bind:title="[todo.completed ? '點選,標記為未完成': '點選,標記為已完成']">✔</button>
</a>
</div>
</div>
<div class="panel-footer">
<form v-on:submit.prevent="add">
<div class="form-group">
<input type="text" class="form-control text-center" v-model="newTodo.title">
</div>
<button class="btn btn-default btn-block" type="submit">新增</button>
</form>
</div>
</div>
</script>
使用元件
<div id="app" class="container">
<div class="row">
<div class="col-md-6">
<todo-items v-bind:initial-todos="initialTodos"></todo-items>
</div>
<div class="col-md-6">
<todo-items v-bind:initial-todos="initialTodos"></todo-items>
</div>
</div>
</div>
new Vue({
el: '#app',
data: {
initialTodos: [
{title: '吃早飯', completed: false},
{title: '吃午飯', completed: false},
{title: '吃晚飯', completed: false}
]
}
});
注意到,兩個元件例項,都賦予了同一個 initialTodos
物件。為了保證每個元件裡的 todos
物件是獨一無二的,所以元件內部 todos
資料都使用了 [].concat(this.initialTodos)
返回的新陣列;否則同一個 Vue 例項下的元件的 todos
資料,會因為引用了同一個物件,行為變一樣了。