0%

Vue for Beginners to Building a Todo App

![](https://raw.githubusercontent.com/keer2345/storehouse/master/hexo/images/2018/10/07.jpg)

本文我们将创建一个基础的 Vue.js,从而学会构建 Todo App 以有助于践学 Vue JS 。

学习框架的有效方式就是践行。

设置

我们从简单的 html 和 css 开始,并且不涉及 JavaScript ,将学习如何使用 Vue JS 添加 JavaScript 功能。

mkdir vue-beginner-todo && cd vue-beginner-todo

yarn init -y

yarn add vue
yarn add vue-router
yarn add todomvc-app-css

yarn add live-server --dev

我们安装了 Vue 和 Vue-router 依赖。我们为项目提供了良好的 CSS 库并添加 live-serve 以使得当代码改变提供来服务和重载页面。这就是我们本文案例所需要的库了。

package.json

{
"name": "vue-beginner-todo",
"version": "1.0.0",
"main": "index.html",
"scripts": {
"serve": "npx live-server --entry-file=index.html",
"start": "npm run serve"
},
"license": "MIT",
"dependencies": {
"todomvc-app-css": "^2.1.2",
"vue": "^2.5.17",
"vue-router": "^3.0.1"
},
"devDependencies": {
"live-server": "^1.2.0"
}
}

index.html

编写 index.html 文件,主要包括:

<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Vue Beginner Todo App</title>
<link rel="stylesheet" href="node_modules/todomvc-app-css/index.css">
</head>

<body>
<div class="app">

</div>


<script src="node_modules/vue/dist/vue.min.js"></script>
<script src="node_modules/vue-router/dist/vue-router.min.js"></script>
<script src="app.js"></script>
</body>

</html>

运行命令,用浏览器打开页面试试:

npm start
# or
yarn start
$ yarn start
yarn run v1.9.4
$ npm run serve

> vue-beginner-todo@1.0.0 serve /home/qinjh/workspace/vuejs/vue-beginner-todo
> npx live-server --entry-file=index.html

Serving "/home/qinjh/workspace/vuejs/vue-beginner-todo" at http://127.0.0.1:8080
Ready for changes

Vue 起步

Vue Data & v-text

./app.js:

const todoApp = new Vue({
el: "#app",
data: {
title: "Vue Todo App"
}
});

./index.html:

<div id="app">
<h1>{{title}}</h1>
<input class="new-todo" placeholder="What needs to be done?" autofocus>
</div>
<!-- ... -->
<h1 v-text="title"></h1>

读取列表 v-for

添加更多有用的数据来丰富我们的 Todo 列表:

const todoApp = new Vue({
el: '.todoapp',
data: {
title: 'Todos',
todos: [
{ text: 'Learn JavaScript ES6+ goodies', isDone: true },
{ text: 'Learn Vue', isDone: false },
{ text: 'Build something awesome', isDone: false },
],
}
});

index.html:

<body>
<div id="app">
<h1>{{title}}</h1>
<input class="new-todo" placeholder="What needs to be done?" autofocus>
<ul class="todo-list">
<li v-for="todo,index in todos">
<div class="view">
<input type="checkbox" class="toggle">
<label>{{todo.text}}</label>
<button class="destroy"></button>
</div>
<input value="Rule the web" class="edit">
</li>
</ul>
</div>

<script src="node_modules/vue/dist/vue.js"></script>
<script src="node_modules/vue-router/dist/vue-router.min.js"></script>
<script src="app.js"></script>
</body>

创建 Todo 以及 event 指令

在 Vue 中,我们可以使用 v-on:EVENT_NAME 来监听事件,例如:

  • v-on:click
  • v-on:dbclick
  • v-on:keyup
  • v-on:keyup.enter

通常,我们使用 @ 来简写,v-on:keyup.enter = @keyup.enter

下面,我们敲击回车来添加一条 Todo

<input class="new-todo" 
placeholder="What needs to be done?"
autofocus @keyup.enter="createTodo">
data: {
// ...
},
methods: {
createTodo(event) {
const textbox = event.target
this.todos.push({ text: textbox.value, isDone: false });
textbox.value = "";
}
}
![](https://raw.githubusercontent.com/keer2345/storehouse/master/hexo/images/2018/10/08.png)

绑定样式 v-bind

<li v-for="todo,index in todos" :class="{completed:todo.isDone}">
<!-- ... -->
</li>

使用 v-model 保持 DOM 与数据的同步

<input type="checkbox" class="toggle" v-model="todo.isDone" >

在浏览器的 console 输入如下指令就可以看到页面上的变化:

todoApp.todos[2].isDone = true

双击修改 Todo 列表

<label @dblclick="startEditing(todo)">{{todo.text}}</label>

const todoApp = new Vue({
el: "#app",
data: {
// ...
editing: ""
},
methods: {
// ...
startEditing(todo) {
this.editing = todo;
}
}
});

我们创建了一个新的变量 editing,当双击 Todo 文本时,我们将当前的 Todo 对象存放到该变量。

接下来,我们使用该变量:

<li v-for="todo,index in todos" :class="{completed:todo.isDone,editing:todo===editing}">
<div class="view">
<input type="checkbox" class="toggle" v-model="todo.isDone">
<label @dblclick="startEditing(todo)">{{todo.text}}</label>
<button class="destroy"></button>
</div>
<input class="edit" @keyup.esc="cancelEditing" @keyup.enter="finishEditing"
@blur="cancelEditing" :value="todo.text">
</li>
finishEditing(event) {
if (!this.editing) {
return;
}
const textbox = event.target;
this.editing.text = textbox.value;
this.editing= null;
textbox.value = "";
},
cancelEditing() {
this.editing = null;
}
![](https://raw.githubusercontent.com/keer2345/storehouse/master/hexo/images/2018/10/09.png)

删除列表

<button class="destroy" @click="destroyTodo(todo)"></button>
destroyTodo(todo) {
const index = this.todos.indexOf(todo);
this.todos.splice(index, 1);
}

加入 trim()

textbox.value.trim()

计算属性 computed

<footer class="footer">
<span class="todo-count">
<strong>{{activeTodos.length}}</strong> item(s) left
</span>
</footer>
computed: {
activeTodos() {
return this.todos.filter(t => !t.isDone);
}
}
![](https://raw.githubusercontent.com/keer2345/storehouse/master/hexo/images/2018/10/10.gif)

清除已完成的任务 v-show

<footer>
<!-- ... -->
<button class="clear-completed" @click="clearCompleted" v-show="completedTodos.length">Clear completed</button>
</footer>

添加方法:

clearCompleted() {
this.todos = this.activeTodos;
}

添加计算属性:

completedTodos() {
return this.todos.filter(t => t.isDone);
}

v-show 与 v-if

v-showv-if 看起来很相似,但确实不同的。v-if 从 DOM 移除元素并且事件不可见,v-show 是通过 CSS 样式 display: none 来隐藏。因此 v-ifv-show 消耗更高一些。

如果我们预先知道某些元素可以触发为不可见状态,通常使用 v-show,反之使用 v-if

如果没有 Todo 列表时,我们可以隐藏着两个部分:

<section class="main" v-if="todos.length">... </section>
<footer class="footer" v-if="todos.length">...</footer>

本地存储

当我们刷新时,列表又被重置了。在开发环境下这样是很有用的,但是并不是我们想要的。让我们来持久化 Todo 列表吧。

localStorage 的工作方式很简单,它是全局变量且只有 4 个方法:

  • localStorage.setItem(key, value)key/value 存储,keyvalue 被强制转换成字符串。
  • localStorage.getItem(key):通过 key 获取项目。
  • localStorage.removeItem(key):删除项目。
  • localStorage.clear():从当前主机上清除所有项目。

首先,我们定义一个存储键:
app.js:

const LOCAL_STORAGE_KEY = 'todo-app-vue';

然后从本地存储替换 data.todos,如果存在的话:

data: {
title: 'Todos',
todos: JSON.parse(localStorage.getItem(LOCAL_STORAGE_KEY)) || [
{ text: 'Learn JavaScript ES6+ goodies', isDone: true },
{ text: 'Learn Vue', isDone: false },
{ text: 'Build something awesome', isDone: false },
],
editing: null,
},

我们使用 JSON.parse ,因为需要将获取到的字符串转换成对象。

Vue Watchers

为了保存数据,我们将使用 Vue Watchers 。

Vue Watchers 与 Computed 属性。计算属性通常用于“计算”和缓存两个或者更多的值。Watchers 比 Couputed 属性更底层,Watchers 允许我们“观察(watch)” 属性的改编。对于一些消耗大的操作时,比如 DB、API 调用等是很有用的。

computed: {
// ...
},
watch: {
todos: {
deep: true,
handle(newValue) {
localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(newValue));
}
}
}

上面的表达式用于观察数据的改变。deep 意思是递归观察数组和对象的值的改变,如果有变化了,我们就将它们保存到本地存储 localStorage

一旦 todos 列表改变了,可以通过浏览器的开发工具看到它们被存储在本地。

![](https://raw.githubusercontent.com/keer2345/storehouse/master/hexo/images/2018/10/11.jpg)

扩展

这样,一个基本的 Todo 列表就制作好了。以后的文章里,我们再来讲述组件、路由和本地存储。

获取这里更多