在了解了 Vue 的一些基本概念以后,就能够写一个最简单的小项目了 --- TodoList。麻雀虽小,五张俱全。虽然是一个小 demo,但也涉及到了组件化、双向绑定、自定义事件的触发与监听、计算属性等概念。接下来从这个小项目中,对这些基本概念进行实践,从而加深理解。html
本文的全部代码在 https://github.com/nodejh/vue2-tutorials/tree/master/02.TodoList。vue
最终实现效果以下:node
接下来就一一实现。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
在 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 会自动从新编译并刷新页面,这时浏览器的页面以下:
在该 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 box
或 span
的时候,就调用 completed
方法并传入被点击的 todo item 的索引。在 completed
方法里面,更新数据对象 data 里面对应的 todo item 的 isCompleted
属性。这样就实现了完成 todo 和取消完成 todo 的功能。点击以后如图:
接下来就须要完成添加新的 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
属性,用来存储用户输入的内容。而后在 template
的 input
输入框里,使用 v-model
实现双向绑定。
当用户按下回车(@keyup.enter="addTodo"
,详见 键值修饰符)或者点击添加按钮(@click="addTodo"
)的时候,就调用 methods
里面的 addTodo
方法。
addTodo
方法经过 vm.$emit
触发了一个 add
事件,并将用户输入的内容(即 this.todo
)做为参数传递。事件触发以后,将输入框中的内容清空。
接下来就须要监听 add
事件了。监听事件须要在使用组件的模板里面,经过 v-on
来实现。详见 使用-v-on-绑定自定义事件。
在 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 数目,以及完成和未完成的数目。
要实现此功能,方法有不少种。最简单的一种是直接在模板中加入 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>
上述代码中经过 completedCounts
和 notCompletedCounts
两个计算属性,来计算出已完成和未完成的 todo。虽然这两个表达式能够直接放在模板中,但表达式比较复杂,看起来也不是很清晰,因此不少时候就能够用计算属性来计算出一个最终值,而后在模板中使用。
到此,基于 Vue 的 Todo List 就完成了。在该项目中,对组件化、双向绑定、自定义事件的触发与监听、计算属性等概念进行了实践。固然,最重要的不是完成这个 Todo List 的代码,而是从实现功能的过程当中触类旁通,经过简单的 demo 实现,去思考如何用 vue 开发一个更大更完整的项目。