一、项目介绍

①地址: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的更多相关文章

  1. openjpa框架入门_项目框架搭建(二)

    Openjpa2.2+Mysql+Maven+Servlet+JSP 首先说明几点,让大家更清楚整体结构: 官方source code 下载:http://openjpa.apache.org/dow ...

  2. openjpa框架入门_项目 database 启动project 初始化(三)

    mysql数据库安装好,这里不多说,现在来执行sql脚本 http://download.csdn.net/detail/shenhonglei1234/6019677 将下载好的脚本后缀名“open ...

  3. openjpa框架入门_openbooks项目Overview(四)

  4. 『Scrapy』爬虫框架入门

    框架结构 引擎:处于中央位置协调工作的模块 spiders:生成需求url直接处理响应的单元 调度器:生成url队列(包括去重等) 下载器:直接和互联网打交道的单元 管道:持久化存储的单元 框架安装 ...

  5. Taurus.MVC 微服务框架 入门开发教程:项目集成:2、客户端:ASP.NET Core(C#)项目集成:应用中心。

    系列目录: 本系列分为项目集成.项目部署.架构演进三个方向,后续会根据情况调整文章目录. 本系列第一篇:Taurus.MVC V3.0.3 微服务开源框架发布:让.NET 架构在大并发的演进过程更简单 ...

  6. 【无私分享:从入门到精通ASP.NET MVC】从0开始,一起搭框架、做项目 目录索引

    索引 [无私分享:从入门到精通ASP.NET MVC]从0开始,一起搭框架.做项目(1)搭建MVC环境 注册区域 [无私分享:从入门到精通ASP.NET MVC]从0开始,一起搭框架.做项目(2)创建 ...

  7. ThinkJS框架入门详细教程(二)新手入门项目

    一.准备工作 参考前一篇:ThinkJS框架入门详细教程(一)开发环境 安装thinkJS命令 npm install -g think-cli 监测是否安装成功 thinkjs -v 二.创建项目 ...

  8. SpringMVC框架入门配置 IDEA下搭建Maven项目(zz)

    SpringMVC框架入门配置 IDEA下搭建Maven项目 这个不错哦 http://www.cnblogs.com/qixiaoyizhan/p/5819392.html

  9. Newbe.Claptrap 框架入门,第二步 —— 创建项目

    接上一篇 Newbe.Claptrap 框架入门,第一步 -- 开发环境准备 ,我们继续了解如何创建一个 Newbe.Claptrap 项目. Newbe.Claptrap 是一个用于轻松应对并发问题 ...

随机推荐

  1. 【数据库】Mysql配置参数

    vim /ect/my.cnf 使用命令打开mysql的配置文件. 加入以下参数 [mysql] default-character-set=utf8 [mysqld] lower_case_tabl ...

  2. C# Winform控件字体大小自适应

    using System.Collections.Generic; using System.Drawing; using System.Windows.Forms; namespace WGClie ...

  3. 使用HttpWebRequest和HttpWebResponse时接收数据中文乱码的情况

    今天在调试一个get请求的接口的时候发现HttpWebResponse方法接收数据的时候,中文出现乱码的情况. 原因是格式编码转码的问题. 原来自从Windows 2000之后的操作系统在文件处理时默 ...

  4. 关于注解sql和动态sql的写法

    注解写sql一般写在mapper层,如果sql语句复杂建议不要写注解sql 拼接容易出错 二动态sql的话要在 main 下面创建一个resource ——mapper—— Mapper.xml 再在 ...

  5. python爬虫---爬虫的数据解析的流程和解析数据的几种方式

    python爬虫---爬虫的数据解析的流程和解析数据的几种方式 一丶爬虫数据解析 概念:将一整张页面中的局部数据进行提取/解析 作用:用来实现聚焦爬虫的吧 实现方式: 正则 (针对字符串) bs4 x ...

  6. 教你玩转Git-合并冲突

    Git 是一个开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目.Git 是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件.Git 与 ...

  7. 使用node+vue实现简单的WebSocket聊天功能

    最近学习了一下websocket的即时通信,感觉非常的强大,这里我用node启动了一个服务进行websocket链接,然后再vue的view里面进行了链接,进行通信,废话不多说,直接上代码吧, 首先, ...

  8. Web前端2019面试总结3(东软集团面试题)

    严禁转载,严禁分享,只供私自鉴赏,请君悉知! 一:基础题 1.什么是margin塌陷?请写出至少三种解决margin塌陷的方法. 答:当两个盒子在垂直方向上设置margin值时,会出现一个有趣的塌陷现 ...

  9. Android-----spinner组件使用(实现下单)

    list view组件和spinner组件使用方法类似,从string.xml中通过entries获取数据显示.但如果要显示的列表项无法在执行前确定,或是要在程序执行的过程中变更选项内容,通过entr ...

  10. springboot如何读取配置文件中的参数(例如:application-consts.properties) 又结合maven读取配置文件的顺序

    1.启动项目后,会读取pom.xml中的配置文件,例如现在读取的是本地配置 2.找到对应的配置文件  会读取uri地址下的配置.注:如果为springboot启动无需加config项目的名称,应该本身 ...