1.前言
TodoMVC是一个示例项目,它使用目前流行的不同JavaScript框架的来实现同一个Demo,来帮助你熟悉和选择最合适的前端框架。官网地址:http://todomvc.com,学习框架最直接有效的方式就是上手练习,接下来我们将用Vue.js来完成TodoMVC的示例。
2.搭建TodoMVC开发环境
2.1 访问官网:http://todomvc.com,点击下载模版。或直接输入:https://github.com/tastejs/todomvc-app-template进行下载
2.2 下载后,项目导入开发工具。目前我使用的是subline。
2.3 打开index.html并预览,会发现js和css文件没导入。这时我们可以使用node安装,cmd进入项目目录输入如下图命令,(在安装前修改package.json文件)
{
"private": true,
"dependencies": {
"director": "^1.2.0",
"vue": "^2.1.8",
"todomvc-common": "^1.0.1",
"todomvc-app-css": "^2.0.0"
}
}
2.4 安装成功后,项目会新增node_modules文件夹。
2.5 打开index.html并在浏览器中预览,可以看见如下图:
3. TodoMVC需要实现的功能需求。
3.1 TodoMVC环境已经搭建成功,接下来需要使用Vue.js来完成一些功能需求。
3.1.1 新增数据
- 在输入框填写新增的值,按Enter键保存。
3.1.2 删除数据
- 点击删除“按钮x”删除选择数据
- 点击右下角“clear complete”清空已complete的数据
3.1.3 修改数据
- 双击某行数据时切换到输入框状态并且自动获得焦点。
- 按“Enter”键或光标离开可以修改数据。
3.1.4 浏览数据
- 当list有值时,可以正常显示数据。
- 左下角“item left”根据当前item数自动变化。
- 点击下方的“all”“active”“complete”可以过滤list,并且a标签高亮自动能切换。
- 勾选item可以切换“active”和“complete”样式。
- 当item全选时,新增数据框左侧的“小箭头”高亮。
- 点击新增数据框左侧的“小箭头”,可以全选或全不选item。
- 进入首页时,新增输入框自动获得焦点。
4.主要代码
4.1 index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Template • TodoMVC</title>
<link rel="stylesheet" href="node_modules/todomvc-common/base.css">
<link rel="stylesheet" href="node_modules/todomvc-app-css/index.css">
<!-- CSS overrides - remove if you don't need it -->
<link rel="stylesheet" href="css/app.css">
</head>
<body>
<section class="todoapp" id="apps">
<header class="header">
<h1>todos</h1>
<input class="new-todo" placeholder="What needs to be done?" v-focuz @keyup.enter="addTodo" >
</header>
<template v-if="fruits.length">
<!-- This section should be hidden by default and shown when there are todos -->
<section class="main" >
<input id="toggle-all" class="toggle-all" @click="toggleall" type="checkbox" v-model = "toggleAllStat.length ===0">
<label for="toggle-all">Mark all as complete</label>
<ul class="todo-list">
<!-- These are here just to show the structure of the list items -->
<!-- List items should get the class `editing` when editing and `completed` when marked as completed -->
<li
v-for="(item,index) in fruitsFilter"
:class="{completed : item.checkstate,editing:item === editstate}">
<div class="view">
<input class="toggle" type="checkbox" v-model="item.checkstate">
<label @dblclick="editstate = item">{{item.name}}</label>
<button class="destroy" @click="removeItem(index)"></button>
</div>
<input class="edit" v-model="item.name" v-dblfocus="item === editstate"
@keyup.enter="editstate = null"
@blur="editstate = null" >
</li>
</ul>
</section>
<!-- This footer should hidden by default and shown when there are todos -->
<footer class="footer">
<!-- This should be `0 items left` by default -->
<span class="todo-count"><strong>{{itemleft}}</strong> item left</span>
<!-- Remove this if you don't implement routing -->
<ul class="filters">
<li>
<a :class="{selected:filterStat===''}" href="#/">All</a>
</li>
<li>
<a :class="{selected:filterStat==='active'}" href="#/active">Active</a>
</li>
<li>
<a :class="{selected:filterStat==='completed'}" href="#/completed">Completed</a>
</li>
</ul>
<!-- Hidden if no completed items are left ↓ -->
<button class="clear-completed" @click="removeALLDone">Clear completed</button>
</footer>
</template>
</section>
<footer class="info" id="apps">
<p>Double-click to edit a todo</p>
<!-- Remove the below line ↓ -->
<p>Template by <a href="http://sindresorhus.com">Sindre Sorhus</a></p>
<!-- Change this out with your name and url ↓ -->
<p>Created by <a href="http://todomvc.com">you</a></p>
<p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
</footer>
<!-- Scripts here. Don't remove ↓ -->
<script src="node_modules/vue/dist/vue.js"></script>
<script src="node_modules/todomvc-common/base.js"></script>
<script src="js/app.js"></script>
</body>
</html>
4.2 app.js
(function (Vue) {
//定义数组
const fruitz = [
{
id : 1,
name : '苹果',
checkstate : true
},
{
id : 2,
name : '香蕉',
checkstate : true
},
{
id : 3,
name : '葡萄',
checkstate : false
}
]
window.vm = new Vue({
el : '#apps',
data:{
fruits : fruitz,
editstate : null,
filterStat : 'all'
},
computed : {
fruitsFilter : function () {
switch(this.filterStat){
case 'active' :
return this.fruits.filter(function (item) {
return item.checkstate === false
})
break;
case 'completed' :
return this.fruits.filter(function (item) {
return item.checkstate === true
})
break;
default :
return this.fruits
break;
}
},
itemleft : function () {
return this.fruitsFilter.length;
},
toggleAllStat : function () {
return this.fruits.filter(item => item.checkstate === false);
}
},
methods:{
toggleall:function (event) {
const check = event.target.checked;
this.fruits.forEach(function(item){
item.checkstate = check;
})
},
addTodo : function (event){
const valueStr = event.target.value;
var lastFruits = this.fruits[this.fruits.length-1];
var idStr = lastFruits ? lastFruits.id+1 : 1;
this.fruits.push({
id : idStr,
name : valueStr,
checkstate : false
})
event.target.value = '';
},
removeItem : function (index) {
this.fruits.splice(index,1);
},
removeALLDone : function () {
this.fruits = this.fruits.filter(function(item){
return !item.checkstate
})
},
editInput:function () {
console.log('editInput');
},
editSave : function (item,event) {
this.editstate = null;
}
},
directives : {
focuz : {
inserted : function (el) {
el.focus();
}
},
dblfocus : {
update : function (el,binding) {
console.log(binding.value);
if(binding.value){
console.log('fff');
el.focus();
}
}
}
}
})
window.onhashchange = function () {
const hashStr = location.hash.replace("#/","");
vm.filterStat = hashStr;
}
})(Vue)