框架入门经典项目TodoMVC
一、项目介绍
①地址:http://todomvc.com/
②GitHub下载模板
③通过npm下载模板的样式

④通过npm下载Vuejs

⑤项目文件,主要修改app.js和index.html两个文件

二、使用Vuejs需求实现(主体思路)
①列表渲染
- 有数据的时候展示出来:v-if 的使用
(function (Vue) {
let todos=[
{id:1,title:'睡觉',completed:true},
{id:2,title:'美食',completed:false},
{id:3,title:'代码',completed:true}
]
new Vue({
el:'#todoapp',
data:{
todos:todos,
},
})(Vue);
<li v-for="item of todos" v-bind:class='{completed:item.completed}'>
<div class="view">
<input class="toggle" type="checkbox" v-model='item.completed'>
<label>{{item.title}}</label>
<button class="destroy"></button>
</div>
<input class="edit" value="Create a TodoMVC template">
</li>
- 没有数据的时候隐藏main部分:添加一个不会出现在页面的template模板,并且使用v-if,当todos没有数据的时候,长度为0
<template v-if='todos.length'>
<!-- This section should be hidden by default and shown when there are todos -->
<section class="main"> ..... </section>
<!-- This footer should hidden by default and shown when there are todos -->
<footer class="footer"> .... </footer>
</template>
②添加任务
- 页面初始化获得焦点:自定义指令,注册一个全局自定义指令 `v-focus`,然后在input里直接使用
// 自定义指令,自动获取焦点
Vue.directive('focus', {
inserted: function (el) {
el.focus();
}
});
<input class="new-todo" placeholder="What needs to be done?" @keyup.enter='addTodo' v-focus>
- 敲回车添加到任务列表:鼠标抬起注册addTodo事件,追加数据
- 不允许有非空数据:为空时,return返回
- 添加完成后清空文本框:令event.target.value= ' '
<header class="header">
<h1>todos</h1>
<input class="new-todo" placeholder="What needs to be done?" @keyup.enter='addTodo'>
</header>
methods:{
// 添加任务
addTodo(event){
let todoText=event.target.value.trim();
if(!todoText.length){
return
}
let id=this.todos[this.todos.length-1].id+1;
this.todos.push({
id:id,
title:todoText,
completed:false,
});
event.target.value='';
},
③标记所有任务完成或者未完成:点击的时候注册toggleAll事件处理函数
<input @click='toggleAll' id="toggle-all" class="toggle-all" type="checkbox">
toggleAll(event){
let checked=event.target.checked;
this.todos.forEach(todo => todo.completed=checked);
},
④任务项
- 切换任务完成状态:v-bind绑定一个class=“{类名:布尔值}”,当布尔值为true,作用这个类名,当布尔值为false,则去除这个类名
<li v-for="item of todos" v-bind:class='{completed:item.completed}'>
- 删除单个任务项:@click=‘ removeTodo(index,$event) ’ ,传入两个参数,删除的索引index和事件$event(传参以后,正常的event获取不到),然后处理函数利用数组方法splice操作
<button class="destroy" @click='removeTodo(index,$event)' ></button>
removeTodo(delIndex,event){
this.todos.splice(delIndex,1);
},
- 双击label进入编辑模式:这里使用一个中间变量currentEditing,默认为null,也就是所有的任务项都没有editing样式,editing的样式取决于中间变量是否等价于当前任务项,当双击的时候,手动把中间量等于双击的当前任务项,这样editing样式就为true,也就是起作用了。
<li v-for="(item,index) of todos" v-bind:class='{completed:item.completed,editing:item===currentEditing}'>
<label @dblclick="currentEditing=item">{{item.title}}</label>
data:{
todos:todos,
currentEditing:null,
⑤编辑任务项
- 编辑文本框自动获得焦点:局部自定义指令,自动获取焦点‘ editing-focus ’
<input class="edit" :value='item.title' @blur='saveEdit(item,index,$event)' @keyup.enter='saveEdit(item,index,$event)' @keyup.esc='currentEditing=null' v-editing-focus="item===currentEditing">
directives:{
// 局部自定义属性
editingFocus:{
update(el,binding){
if(binding.value){
el.focus();
}
},
},
},
- 在编辑文本框敲回车后者失去焦点后,如果为空,则直接删除这个item,如果不为空,保存这个数据,并去除editing样式:saveEdit处理函数,传入参数
- 输入状态按下esc取消编辑:设置默认value属性是item的title,按下esc抬起的时候,令中间变量为null,去除editing样式
<input class="edit" :value='item.title' @blur='saveEdit(item,index,$event)' @keyup.enter='saveEdit(item,index,$event)' @keyup.esc='currentEditing=null'>
saveEdit(item,index,event){
var editText=event.target.value.trim();
// 如果为空,直接删除这个item
if(!editText.length){
return this.todos.splice(index,1);
}
// 如果不为空,修改title的值,然后去除eiditing样式
item.title=editText;
this.currentEditing=null;
},
⑥其他(footer部分)
- 显示所有未完成任务数:@click=‘ removeAllDone ’ ,处理事件利用数组方法filter过滤未完成数据,然后重新赋值给数据列表
<button class="clear-completed" @click='removeAllDone'>Clear completed</button>
removeAllDone(){
this.todos=this.todos.filter((item,index)=>{
return !item.completed;//return true,即item.completed为false
});
},
- 清除所有的已完成任务:利用计算属性computed的自定义方法leftCount(参考vue教程--计算属性),还有一种方法就是模板中调用处理函数,处理函数使用for循环来删除,但是删完需要把循环索引i--,但是这种方法没有缓存,每一次使用都要重新调用,推荐使用计算属性,效率更高。
<span class="todo-count"><strong>{{leftCount}}</strong> item left</span>
computed:{
leftCount:function(){
return this.todos.filter(item => !item.completed).length
}
},
- 将数据持久化到localStorage中(待完成):利用watch功能(配置deep,深度监视),计算属性用于需要在模板中绑定输出值,而watch观察者则用于根据需要数据的改变从而定制特殊功能业务
- 路由状态切换:data里添加属性filterState默认为‘all’;计算属性computed增加filtertodos方法,过滤不同状态的路由;同时修改列表渲染为遍历filterTodos;在window里添加路由改变事件onhashchange,并且每次页面进来需要执行一次保持上一次的状态;改变点击时的样式,添加属性selected当为true时作用,即filterState会等于路由的时候,样式生效。
data:{
todos:todos,
currentEditing:null,
filterState:'all',
},
computed:{
leftCount:function(){
return this.todos.filter(item => !item.completed).length
},
filterTodos:function(){
switch(this.filterState){
case 'active':
return this.todos.filter(item=>!item.completed);
break;
case 'completed':
return this.todos.filter(item=>item.completed);
break;
default:
return this.todos;
break;
};
},
<li v-for="(item,index) of filterTodos" v-bind:class='{completed:item.completed,editing:item===currentEditing}'>
// 路由状态切换
window.onhashchange=function(){
var hash=window.location.hash.substr(2) || 'all';
window.app.filterState=hash;
};
// 页面第一次进来,保持状态
window.onhashchange();
<ul class="filters">
<li>
<a :class="{selected:filterState==='all'}" href="#/">All</a>
</li>
<li>
<a :class="{selected:filterState==='active'}" href="#/active">Active</a>
</li>
<li>
<a :class="{selected:filterState==='completed'}" href="#/completed">Completed</a>
</li>
</ul>
三、项目完整代码和效果展示
<!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>
<!-- id="todoapp"vue管理模块入口 -->
<section id="todoapp" class="todoapp">
<header class="header">
<h1>todos</h1>
<input class="new-todo" placeholder="What needs to be done?" @keyup.enter='addTodo' v-focus>
</header>
<template v-if='todos.length'>
<!-- This section should be hidden by default and shown when there are todos -->
<section class="main">
<!-- @click='toggleAll'点击事件 -->
<input @click='toggleAll' id="toggle-all" class="toggle-all" type="checkbox" v-bind:checked='toggleState'>
<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 -->
<!-- vue列表渲染 -->
<li v-for="(item,index) of filterTodos" v-bind:class='{completed:item.completed,editing:item===currentEditing}'>
<div class="view">
<input class="toggle" type="checkbox" v-model='item.completed'>
<label @dblclick="currentEditing=item">{{item.title}}</label>
<button class="destroy" @click='removeTodo(index,$event)' ></button>
</div>
<input class="edit" :value='item.title' @blur='saveEdit(item,index,$event)' @keyup.enter='saveEdit(item,index,$event)' @keyup.esc='currentEditing=null' v-editing-focus="item===currentEditing">
</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>{{leftCount}}</strong> item left</span>
<!-- Remove this if you don't implement routing -->
<ul class="filters">
<li>
<a :class="{selected:filterState==='all'}" href="#/">All</a>
</li>
<li>
<a :class="{selected:filterState==='active'}" href="#/active">Active</a>
</li>
<li>
<a :class="{selected:filterState==='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">
<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/todomvc-common/base.js"></script>
<script src="node_modules/vue/dist/vue.js"></script>
<script src="js/app.js"></script>
</body>
</html>
index.html
(function (Vue) {
// 数据
let todos=[
{id:1,title:'睡觉',completed:true},
{id:2,title:'美食',completed:false},
{id:3,title:'代码',completed:true}
];
// 全局自定义指令,自动获取焦点
Vue.directive('focus', {
inserted: function (el) {
el.focus();
}
});
// vue实例
window.app=new Vue({
el:'#todoapp',
data:{
todos:todos,
currentEditing:null,
filterState:'all',
toggleAllstate:true,
},
computed:{
leftCount:function(){
return this.todos.filter(item => !item.completed).length
},
filterTodos:function(){
switch(this.filterState){
case 'active':
return this.todos.filter(item=>!item.completed);
break;
case 'completed':
return this.todos.filter(item=>item.completed);
break;
default:
return this.todos;
break;
};
},
// 全选的联动效果
toggleState:function(){
return this.todos.every(item=>item.completed);
},
},
methods:{
// 添加任务
addTodo(event){
let todoText=event.target.value.trim();
if(!todoText.length){
return
}
const lastTodo=this.todos[this.todos.length-1];
const id=lastTodo?lastTodo.id+1:1;
this.todos.push({
id:id,
title:todoText,
completed:false,
});
event.target.value='';
},
// 点击全部完成或者未完成
toggleAll(event){
let checked=event.target.checked;
this.todos.forEach(todo => todo.completed=checked);
},
// 删除单个任务项
removeTodo(delIndex,event){
this.todos.splice(delIndex,1);
},
// 显示所有未完成任务数(删除所有已完成)
removeAllDone(){
this.todos=this.todos.filter((item,index)=>{
return !item.completed;//return true,即item.completed为false
});
},
// 保存编辑项
saveEdit(item,index,event){
var editText=event.target.value.trim();
// 如果为空,直接删除这个item
if(!editText.length){
return this.todos.splice(index,1);
}
// 如果不为空,修改title的值,然后去除eiditing样式
item.title=editText;
this.currentEditing=null;
},
},
directives:{
// 局部自定义属性
editingFocus:{
update(el,binding){
if(binding.value){
el.focus();
}
},
},
},
});
// 路由状态切换
window.onhashchange=function(){
var hash=window.location.hash.substr(2) || 'all';
window.app.filterState=hash;
};
// 页面第一次进来,保持状态
window.onhashchange();
})(Vue);
app.js

框架入门经典项目TodoMVC的更多相关文章
- openjpa框架入门_项目框架搭建(二)
Openjpa2.2+Mysql+Maven+Servlet+JSP 首先说明几点,让大家更清楚整体结构: 官方source code 下载:http://openjpa.apache.org/dow ...
- openjpa框架入门_项目 database 启动project 初始化(三)
mysql数据库安装好,这里不多说,现在来执行sql脚本 http://download.csdn.net/detail/shenhonglei1234/6019677 将下载好的脚本后缀名“open ...
- openjpa框架入门_openbooks项目Overview(四)
- 『Scrapy』爬虫框架入门
框架结构 引擎:处于中央位置协调工作的模块 spiders:生成需求url直接处理响应的单元 调度器:生成url队列(包括去重等) 下载器:直接和互联网打交道的单元 管道:持久化存储的单元 框架安装 ...
- Taurus.MVC 微服务框架 入门开发教程:项目集成:2、客户端:ASP.NET Core(C#)项目集成:应用中心。
系列目录: 本系列分为项目集成.项目部署.架构演进三个方向,后续会根据情况调整文章目录. 本系列第一篇:Taurus.MVC V3.0.3 微服务开源框架发布:让.NET 架构在大并发的演进过程更简单 ...
- 【无私分享:从入门到精通ASP.NET MVC】从0开始,一起搭框架、做项目 目录索引
索引 [无私分享:从入门到精通ASP.NET MVC]从0开始,一起搭框架.做项目(1)搭建MVC环境 注册区域 [无私分享:从入门到精通ASP.NET MVC]从0开始,一起搭框架.做项目(2)创建 ...
- ThinkJS框架入门详细教程(二)新手入门项目
一.准备工作 参考前一篇:ThinkJS框架入门详细教程(一)开发环境 安装thinkJS命令 npm install -g think-cli 监测是否安装成功 thinkjs -v 二.创建项目 ...
- SpringMVC框架入门配置 IDEA下搭建Maven项目(zz)
SpringMVC框架入门配置 IDEA下搭建Maven项目 这个不错哦 http://www.cnblogs.com/qixiaoyizhan/p/5819392.html
- Newbe.Claptrap 框架入门,第二步 —— 创建项目
接上一篇 Newbe.Claptrap 框架入门,第一步 -- 开发环境准备 ,我们继续了解如何创建一个 Newbe.Claptrap 项目. Newbe.Claptrap 是一个用于轻松应对并发问题 ...
随机推荐
- BZOJ3277 串(后缀自动机)
对多串建立SAM的一种方法是加分隔符.于是加完分隔符建出SAM. 考虑统计出每个节点被多少个串包含.让每个串各自在SAM上跑,跑到一个节点就标记(显然一定会完全匹配该节点,因为是对包含其的串建的SAM ...
- Queue介绍
美人如斯! 前言 队列是一种先进先出(FIFO)的数据结构,与生活中的排队类似,即先来先被服务,这样的特点决定了其具有一定的优先级含义,可以被用于任务调度等场景.队列模型如图: 图1.队列模型 jav ...
- docker离线导入导出镜像
docker save -o mysqlies.tar 5ada6380f248(镜像id) # 镜像id是要导出的 有镜像的机器 在有镜像的机器上执行docker load --input ...
- N(C)O(S)I(P)P 2019 退役记
N(C)O(S)I(P)P 2019 退役记 day-4 今天下午老师突然咕了,于是一下午欢乐时光 今天上午考试T3线段树维护个区间加,区间乘 一遍过编译,一遍过样例(第一次,俺比较弱(虽然也发现和暴 ...
- 第二章 Maven 构建 Java Web项目
本教程中,我们将教给大家如何创建一个基于Spring MVC 的 Maven Web Project. 用到的技术/工具: 1.Eclipse 4.9 2.Maven 3.6.2 3.Tomcat 8 ...
- 面试题:android的安全机制有哪些
1 uid . gid . gids Android 的权限分离的基础是建立在 Linux 已有的 uid . gid . gids 基础上的 . UID: Android 在 安装一个应用程序,就会 ...
- Android-----File(文件各种操作)
在Android编程中,操作文件是基本的必备技能,现在做一个操作文件的小工具:DocumentTool.java 代码如下: package com.hs.example.exampleapplica ...
- Servlet , GenericServlet和HttpServlet
Servlet是一套规范,表现为一套接口,留给开发人员去实现,Servlet接口定义如下(附加servlet-api source来查看源码) 其中init方法被Servlet容器调用,servlet ...
- 【C++编程基础】(1)—— 函数原型声明、函数模板、引用、const 常引用、const 常量指针
一.函数原型声明: 1.函数声明告诉编译器函数的名称,和如何调用函数(返回类型和参数):函数定义提供了函数的实际主体. 2.强制性的:在C++中,如果函数调用的位置在函数定义之前,则要求在函数调用之前 ...
- prometheus学习系列六: Prometheus relabel配置
relabel_config 重新标记是一个功能强大的工具,可以在目标的标签集被抓取之前重写它,每个采集配置可以配置多个重写标签设置,并按照配置的顺序来应用于每个目标的标签集. 目标重新标签之后,以_ ...