jrg-project-5
jrg-project-5 copied to clipboard
Get the shit done!
首先新建项目目录
cp -r step-2 step-3
这样我们就可以基于之前的代码做新任务,同时不改动之前的代码。
学会一个框架的最好办法
学会一个框架的最好办法那就是——做毁一个项目。
接下来我们就要用 Vue.js 做一个待办事项小应用。简单起见,我们就不写 CSS 了,只用 HTML 和 JS 搞定。
我们的目标只有一个,就是搞清楚怎样用 Vue.js 进行开发。
需求
这个项目的英文名就暂定为 Todo,它有以下功能:
- 用户可以新建一个待办事项
- 用户可以删除一个待办事项
- 用户可以将一个待办事项标记为已完成
- 用户刷新页面之后,待办事项还在
Getting Started
由于我们现在对 Vue.js 还一无所知,所以就走一步算一步先。
首先我们用 HTML 描绘一下我们的界面
page.html
<html>
<head>
<meta charset=utf-8>
</head>
<body>
<div id="app">
<div class="newTask">
<input type="text">
</div>
<ol class="todos">
</ol>
</div>
<script src="bundle.js"></script>
</body>
</html>
- charset 要加上,不然出现中文就乱码了
- 加一个
div#app,用于给 Vue 初始化 div.newTask > input用于让用户输入待办的内容ol.todos用于容纳所有待办,每个待办就是一个<li>
添加待办
接下来我们做第一个需求,添加待办。
做之前你要想好流程:
- 用户输入待办内容
- 用户按下回车
- 新的待办出现在
ol.todos里
好的,开始做了。
import Vue from 'vue'
var app = new Vue({
el: '#app',
data: {
newTodo: '',
todoList: []
}
})
我们用 todoList 数组作为所有待办事项的容器,newTask 作为 input 的值。
为什么要有 data?
这里出现了第一个令我们费解的地方——「为什么我们需要将 DOM 与 JS 变量(data)对应起来」。
如果我们用 jQuery 来写,直接在 input 的键盘事件中取出 input.value,构造一个 <li>,插入到 ol.todos 就完了嘛。对不对?
这就是框架和库的区别了。jQuery 作为一个库,你想怎么用就怎么用,但是你在使用一个框架的时候,有很多「指导思想」是你要遵循的。Vue 的指导思想之一就是「尽量不要操作 DOM」,因为这个框架会帮你操作 DOM。
绑定数据
<div class="newTask">
<input type="text" v-model="newTodo">
</div>
这一句将 input.value 与 data.newTodo 绑定起来了,而且是双向的:
- 只要 input.value 被用户改了,data.newTodo 就会变成一样的值;
- 只要 data.newTodo 被 JS 改了,input.value 就会变成一样的值。
怎么验证呢?
首先我们来验证在 JS 里改变 newTodo,input.value 就会变:
import Vue from 'vue'
var app = new Vue({
el: '#app',
data: {
newTodo: '',
todoList: []
},
created: function(){
let i = 0
setInterval(()=>{
this.newTodo = i // this.newTodo 就是 data.newTodo,实际上 this.newTodo 是 data.newTodo 的代理
i+= 1
},1000)
}
})
运行 webpack,打开 page.html,可以看到 input 的值自己变化着。
Tips:如果你不想每次都运行 webpack,那么你可以新开一个命令行窗口,运行 webpack --watch,那么 webpack 就会在每次 JS 文件变化时自动重新运行。
接下来验证 input.value 改变会导致 data.newTodo 变化:
import Vue from 'vue'
var app = new Vue({
el: '#app',
data: {
newTodo: '',
todoList: []
},
created: function(){
setInterval(()=>{
console.log(this.newTodo)
},1000)
}
})
F12 打开 console,然后在 input 里输入一些字符试试。
以上,就是双向绑定。
细节请自行查看 https://cn.vuejs.org/v2/guide/forms.html
绑定事件
我们需要在用户敲击 回车 的时候,在 data.todoList 里新建一个对象。
如何监听用户的键盘事件呢?请查看 https://cn.vuejs.org/v2/guide/events.html
看完这一节,你就能写出以下代码了:
app.js
import Vue from 'vue'
var app = new Vue({
el: '#app',
data: {
newTodo: '',
todoList: []
},
methods: {
addTodo: function(){
this.todoList.push({
title: this.newTodo,
createdAt: new Date()
})
console.log(this.todoList)
}
}
})
page.html
<div class="newTask">
<input type="text" v-model="newTodo" @keypress.enter="addTodo">
</div>
这时你刷新 page.html,在 input 里面输入 回车,就会在控制台看到 todoList 不是空字符串了:
展示新待办
虽然 data.todoList 已经含有一个新的项目了,但是页面里却没有展示。
根据 https://cn.vuejs.org/v2/guide/list.html 写出下面代码:
page.html
<ol class="todos">
<li v-for="todo in todoList">
{{ todo.title }}
</li>
</ol>
然后重新刷新页面,在 input 输入一些字符,回车。你就会看到新增成功了:

优化
按照正常人的逻辑,添加成功后,input 的值应该清空,于是我们改写 app.js:
app.js
methods: {
addTodo: function(){
this.todoList.push({
title: this.newTodo,
createdAt: new Date()
})
this.newTodo = '' // 变成空
}
}
刷新试试效果如何吧。

标记为完成
思路:
- 给每一个 todo 添加一个 done 属性
- 给每一个
<li>里面添加一个 checkbox - 参考 https://cn.vuejs.org/v2/guide/forms.html#复选框 ,将 done 和 checkbox 双向绑定。
代码如下:
app.js
methods: {
addTodo: function(){
this.todoList.push({
title: this.newTodo,
createdAt: new Date(),
done: false // 添加一个 done 属性
})
this.newTodo = ''
}
}
page.html
<ol class="todos">
<li v-for="todo in todoList">
<input type="checkbox" v-model="todo.done"> {{ todo.title }}
<span v-if="todo.done">已完成</span>
<span v-else>未完成</span>
</li>
</ol>
效果如下:

删除待办
思路:
- 在每一项后面添加一个删除按钮
- 点击按钮则从 data.todoList 中删除该项
代码如下:
app.js
methods: {
addTodo: function(){
this.todoList.push({
title: this.newTodo,
createdAt: new Date(),
done: false // 添加一个 done 属性
})
this.newTodo = ''
},
// 加了👇这个函数
removeTodo: function(todo){
let index = this.todoList.indexOf(todo) // Array.prototype.indexOf 是 ES 5 新加的 API
this.todoList.splice(index,1) // 不懂 splice?赶紧看 MDN 文档!
}
}
page.html
<ol class="todos">
<li v-for="todo in todoList">
<input type="checkbox" v-model="todo.done"> {{ todo.title }}
<span v-if="todo.done">已完成</span>
<span v-else>未完成</span>
<button @click="removeTodo(todo)">X</button> <!-- 👈 加了一个按钮 -->
</li>
</ol>
效果如下(GIF有点大,请稍等或开代理)

保存待办事项
我们发现每次刷新页面,待办就没了。
这是因为这些代码都保存在内存里,而内存是无法持久的。所以我们选择保存在 localStorage 中。
思路:
- 在用户关闭页面前,将数据保存在 localStorage 里
- 在用户进入页面后,立刻读取 localStorage
代码如下:
app.js
var app = new Vue({
el: '#app',
data: {
newTodo: '',
todoList: []
},
created: function(){
// onbeforeunload文档:https://developer.mozilla.org/zh-CN/docs/Web/API/Window/onbeforeunload
window.onbeforeunload = ()=>{
let dataString = JSON.stringify(this.todoList) // JSON 文档: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/JSON
window.localStorage.setItem('myTodos', dataString) // 看文档https://developer.mozilla.org/zh-CN/docs/Web/API/Window/localStorage
}
let oldDataString = window.localStorage.getItem('myTodos')
let oldData = JSON.parse(oldDataString)
this.todoList = oldData || []
},
methods: { ...
由于我们只涉及数据的变化,所以 page.html 不变。
让我看到你的应用
你可以将这个应用部署到 GitHub Pages 上。
- 新建一个 GitHub repo,或者使用你现有的 GitHub repo。
- 在 repo 的 Settings 页面里将 GitHub Pages 功能打开,并选中 master 分支,点 Save,你就会得到一个「部署地址」,比如我的地址是 https://jirengu-inc.github.io/jrg-project-5/:

- 在这个部署地址后面接上你 repo 里的文件路径,也可以预览 HTML 了。比如我的预览页面是:https://jirengu-inc.github.io/jrg-project-5/step-3/page.html
- 由于国内访问 GitHub 较慢,所以你预览的时候可能看到
{{ }}标记,不要紧,等一会就好。如果你不想让用户看见这些,可以看 https://cn.vuejs.org/v2/api/#v-cloak
致饥人谷学员
Just get this shit done.
把你的预览页面发到下面,你就成功了。
如果还有时间,建议看看 TodoMVC 项目。比我们这个应用复杂一丢丢,你已经可以独自完成 TodoMVC 了,如果你完成了,把你的链接放在下面单独 at 我。
挑战
- 你能否把 newTodo 的内容也保存下来,下次用户进入页面时,会显示之前输入但是还未提交的 newTodo 内容?
- 你能否把这个页面美化一下?
- 你能否添加「友好的」时间展示,让用户知道这个 todo 是什么时候创建的。
代码在这: https://github.com/jirengu-inc/jrg-project-5/tree/master/step-3
@wlf1112 bug 1: 刷新页面后待办就木有了~
老师,改完了@FrankFang
@lzm320856 无法将多个标记为已完成,报错:
Uncaught SyntaxError: Identifier 'CANDIDATE_MIME_TYPES' has already been declared
at (index):1
@FrankFang 已修改
@zhangjiuyi 界面很小清新哦
@WangXiaoyugg 永远不要给中文字加斜体样式
@lightbuild 使用 Date.prototype.Format 改写 Date 不太好,你可以用一个 formatDate 函数来做
function formatDate(date, format){...}
@ab690257072 BUG 1:你在页面上操作一段时间之后,就会发现新增的待办默认是「已完成」的。
@FrankFang 已修改,麻烦方方老师啦
@wlf1112 都是自己写的么,很棒啊
我参考官网的了@FrankFang
@FrankFang 方方老师辛苦了,已修改。
