一、在github中下载TodoMVC的模板:

1、TodoMVC官网:http://todomvc.com/

2、模板下载的GitHub网址:https://github.com/tastejs/todomvc-app-template

方式一:使用git下载到项目文件夹:

git clone https://github.com/tastejs/todomvc-app-template.git

方式二:直接下载zip压缩包:

二、nmp 安装依赖:

1、进入项目所在文件夹下:

2、使用命令:npm install,回车安装。

注意:要使用 npm 命令,需要安装 node.js 环境才可用。node.js 环境安装请自行百度。

三、在VS code中打开项目文件夹:

打开index.html文件,在浏览器中打开测试一下:

四、初始化项目:

1、下载vue依赖模块:

在VS code中打开终端,进入到项目所在文件夹下,使用以下命令安装vue:

npm install vue@2.6.10

2、引入vue.js到index.html文件中:

3、找到index.html中要被vue管理的父元素:<section>标签

注:下方<footer>标签为页面下方提示信息,只是静态显示,不需要vue操作。

4、为<section>标签取个id名:id="todoapp",作为vue管理的el入口。

5、模板中默认引入了app.js文件,作为vue.js的代码存入的地方,因此,在app.js中创建vue的实例对象。

打开index.html测试:

五、数据列表渲染:

1、声明静态事件项:

2、找到事件项所在的元素上:

3、v-for迭代输出<li>元素:

4、模板中定义了一个completed的样式,这个样式会使已经完成的项目文字上有删除线:

现在要基于事件项前面的勾选按钮来控制completed样式的生效与否,在completed样式上进行属性绑定v-bind:

:class={key为class样式名,value为获取的数据true或false}

5、事件项后删除按钮绑定事件项的id:

要为删除按钮绑定事件,通过获取事件项的id值来确定删除的是哪一项:

6、若没有事件项,则事件项下方的操作按钮应该设置为隐藏:

在浏览器中找到下方的操作按钮所在元素标签:

使用v-show通过对数组中的长度判断,来设置<section>和<footer>中的内容显示与否:

当items.length = 0,则解析为:v-show = false,则标签中的内容不显示,否则显示。

测试:注释掉items中的数组,查看浏览器:

六、添加任务:

1、在最上面的文本框中添加新的事件项:

找到文本框所在的input标签:

在app.js中添加函数方法:

2、获取输入框中的输入的值:

使用event事件操作DOM,来获取输入框中的值:

最后在浏览器控制台查看,其中value值在target下:

因此获取value的值只需要:e.target.value。

3、将输入的事件项添加到未完成事件项中:

①若输入为空或者为空格,则不做操作;(判断是否为空可以使用.trim()函数,可以去除空格)

②若不为空则添加到事件列表中,默认状态为未完成。(添加操作可以使用.push()函数)

4、添加完成后清除输入框中的内容:

注:此实例数据是提前声明的,未用到数据库,因此添加事件项后若刷新页面,依旧是显示没添加新数据之前的网页。解决方法是将数据保存在数据库中,然后对数据库进行增删改查来改动数据。

七、显示所有未完成数据:

1、左下角显示未完成事件项的个数:

2、查找“显示所有未完成数据”这一操作在什么标签元素下:

3、双向绑定:

4、定义计算属性,检测未完成数量:(使用.filter数组函数过滤事件项)

其中filter()中有一个回调函数,定义一个i用来接收items数组中的数组项,然后i.completed获取“未完成的事件项”的数组并返回,在赋值给unitems数组,最后返回unitems数组的长度,也就是未完成的事件项的个数。

5、调节item细节:

当未完成事件项数为0或者>1个时,页面中显示为items,

当未完成事件项数为1时,页面中显示为item。

八、改变所有任务状态:

1、单机输入框左边的复选框按钮可以切换所以事件项的状态:

2、实现方法,添加计算属性,设置set/get方法,双向监听:

①查找复选框所在元素标签:

②双向绑定,添加计算属性:

③使用set/get:

注:计算属性如果不设置get/set,则默认是用 get ,可以把值返回出去。

set 方法可以有一个参数,这里我定义为 newStatus,在这段代码中则就是函数 toggleAll 的值。

九、移除事件项:

1、悬停在某个事件项上显示 X 移除按钮,可点击移除当前事件项。

2、实现方法:通过数组函数 splice() 移除事件项

①查找X按钮所在标签元素:

②绑定事件,添加时间函数:

③获取删除的事件项的索引值并在点击函数中加入:(删除哪个事件项的id)

④删除所选事件项:

首先介绍splice()函数用法:

arrayObject.splice(index,howmany,item1,.....,itemX);

参数 描述
index 必需。整数,规定添加/删除项目的位置,使用负数可从数组结尾处规定位置。
howmany 必需。要删除的项目数量。如果设置为 0,则不会删除项目。
item1, ..., itemX 可选。向数组添加的新项目。

此例中,不需要加入新的数组,即不需要第三个参数。

因此:

十、一键删除已完成的所有事件项:

1、功能分析:

右下角有Clear completed按钮:

当没有已完成事件项,则此按钮隐藏,否则显示;

当点击此按钮时,可以清除已选择的已完成事件项。

2、查找按钮所在元素标签:

3、添加点击事件,用filter()函数过滤:

4、v-show设置按钮的隐藏显示:

注:remain为上面第七步的计算属性中的一个方法函数,返回值是未完成的事件数量。

十一、双击编辑事件项:

1、查找事件项所在元素:

2、双击(dblclick) label进入编辑状态:(在<li>上通过 .editing 进行切换状态)

①添加双击事件:

②在<li>上添加editing 属性:

currentItem是获取当前点击的事件项:需要在app.js中声明currentItem。

3、进入编辑状态后,输入框中显示的是原内容:

4、进入编辑状态后,按ESC按钮退出编辑状态:

注意:必须要在光标获取到焦点后,按ESC按键才能取消编辑状态。解决方法:定义进入编辑状态,自动获取焦点。

5、按Enter键或失去焦点时,数据自动保存,若输入为空,则清除该事件项:

6、自定义指令自动获取焦点(包括主输入框和双击事件项进入编辑状态皆自动获取焦点):

7、为主输入框和双击事件项进入编辑状态添加自动获取焦点属性:

注:当为主输入框添加自动获取焦点属性,测试可以成功,但为“双击事件项进入编辑状态”添加自动获取焦点属性则测试失败,原因是在定义自定义指令时,用的是inserted钩子函数,此方法只第一次插入时才调用。

因此,才有update钩子函数:update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是可以通过比较更新前后的值来忽略不必要的模板更新。

8、判断是否是当前点击的事件项:

另:自定义局部指令获取焦点:

十二、过滤不同状态数据:

本例中事件项列表下方有三个按钮:

分别代表:All显示所有;Active显示未完成;Completed显示已完成。

1、在 data 中定义变量 filterStatus , 用于接收变化的状态值:

2、通过 window.onhashchange获取点击的路由hash,来获取对应的那个状态值,并将状态值赋值给filterStatus:

3、在Vue的计算属性中创建filter方法函数用于过滤出目标数据, 用于感知filterStatus的状态值变化:

4、在index.html文件中循环遍历当filterStatus状态值不同时的事件项列表:

5、根据当前所在hash页面,改变对应的按钮样式:class="selected"

十三、数据保存到本地localStorage:

之前在第六步已然提及:

此实例数据是提前声明的,未用到数据库,因此添加事件项后若刷新页面,依旧是显示没添加新数据之前的网页。解决方法是将数据保存在数据库中,然后对数据库进行增删改查来改动数据。

当然还可以将所有任务项数据持久化到localStorage中,用于本地存储数据。

思路:使用 Vue 中的 watch 监听器,监听任务数组items一旦有改变,则使用 window.localStorage 将它就重新保存到 localStorage。

1、定义 watch 监听器:

因为要监听items数组中的每个对象和每个对象下的每个属性的改变,但watch监听器无法做到,因此要使用命令式的 vm.$watch API深度监听。

使用方法参见中文文档:https://cn.vuejs.org/v2/api/#vm-watch

items数组内部是对象,当对象的值发生变化后要被监听到,在选项参数中使用deep: true。

2、将数据保存到本地localStorage:(localStorage用法详见:https://www.runoob.com/jsref/prop-win-localstorage.html)

①初始化数据操作:

localStorage.getItem(STORAGE_KEY)||'[]'   //获取items中的字符串值,若为空则返回[]。

②调用方法,将数据保存到本地:

1)更改当前初始化items数据:

2)将数据保存到本地:

十四、附:所有代码

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="todoapp"><header class="header"><h1>{{title}}</h1><input @keyup.enter = "addItem" class="new-todo" placeholder="What needs to be done?" v-focus></header><!-- This section should be hidden by default and shown when there are todos --><section class="main" v-show="items.length"><input v-model="toggleAll" id="toggle-all" class="toggle-all" type="checkbox"><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 filter" :class = "{completed:item.completed,editing:item === currentItem}" ><div class="view"><!-- v-model双向绑定 --><input class="toggle" type="checkbox" v-model="item.completed"><!-- 修改事件项的内容 --><label @dblclick="dblc(item)">{{item.content}}</label><button @click = "removeItem(index)" class="destroy" :value = "item.id"></button></div><input v-focus="item === currentItem" @keyup.enter="enter(item,index,$event)" @blur="enter(item,index,$event)" @keyup.esc="esc" class="edit" :value="item.content"></li></ul></section><!-- This footer should hidden by default and shown when there are todos --><footer class="footer" v-show="items.length" ><!-- This should be `0 items left` by default --><span class="todo-count"><strong>{{remain}}</strong> item{{remain === 1?'':'s'}} left</span><!-- Remove this if you don't implement routing --><ul class="filters"><li><a :class="{selected:filterStatus==='all'}" href="#/">All</a></li><li><a :class="{selected:filterStatus==='active'}" href="#/active">Active</a></li><li><a :class="{selected:filterStatus==='completed'}" href="#/completed">Completed</a></li></ul><!-- Hidden if no completed items are left ↓ --><!-- 当总任务数大于未完成任务数,则显示按钮 --><button v-show="items.length > remain" @click="clearItems" class="clear-completed">Clear completed</button></footer></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/vue/dist/vue.js"></script><script src="node_modules/todomvc-common/base.js"></script><script src="js/app.js"></script></body>
</html>

2、app.js

(function (Vue) {//声明一个key值const STORAGE_KEY = 'item';//声明变量获取保存本地数据const localstorage ={//获取数据方法fetch:function(){//JSON.parse将字符串转换为数组对象return JSON.parse(localStorage.getItem(STORAGE_KEY)||'[]')},//保存数据方法save:function(items){//JSON.stringify(value)将value中的内容转成字符串localStorage.setItem(STORAGE_KEY,JSON.stringify(items))}}const items = [{id:1,                    //编号content:'今天早上学JS',     //事件内容completed:false          //完成状态},{id:2,                  //编号content:'今天下午学HTML',//事件内容completed:false         //完成状态},{id:3,                  //编号content:'今天晚上学CSS', //事件内容completed:false         //完成状态}]//自定义全局指令,自动获取焦点Vue.directive('focus',{inserted(el,binding){el.focus();},update(el,binding) {if(binding.value)el.focus();},})var vm = new Vue({el:'#todoapp',data:{title:'myList',//items:items,       // ES6中可直接写一个items等价于——>items:itemsitems:localstorage.fetch(),currentItem:null,filterStatus:'all'},//自定义局部指令,自动获取焦点//directives:{//    'focus':{//       update(el,binding) {//          if(binding.value)//         el.focus();//       },//    }//},computed: {//在Vue的计算属性中创建filter方法函数用于过滤出目标数据, 用于感知filterStatus的状态值变化:filter(){switch (this.filterStatus) {case 'active':return this.items.filter(function(i){return   !i.completed;})break;case 'completed':return this.items.filter(i=>i.completed);break;//否则默认返回所有事件项default:return this.items;break;}},toggleAll:{//当事件列表中的状态发生变化,更新复选框状态get(){//恒等式左边代表未完成的个数,右边恒为0,只有两边恒等,则返回true//否则返回falsereturn this.remain === 0;},//当复选框状态发生变化,更新任务列表所有事件项的状态set(newStatus){//遍历出数组中所有事件项//将当前复选框的状态赋值给每个事件项的completed状态this.items.forEach(function(i){i.completed = newStatus;})}},remain(){    //ES6简写const unitems = this.items.filter(function(i){return i.completed === false;});return unitems.length;}},methods: {//完成编辑,保存数据enter(item,index,e){//1获取当前输入框的值const content = e.target.value.trim();//2判断输入框的值是否为空,如果为空,删除事件项if(!content){//复用下面的removeItem()函数this.removeItem(index);return;}//3否则,更新事件项else{item.content = content;}//4取消editing的作用,退出编辑状态this.currentItem = null;},//取消编辑状态esc(){//当this.currentItem为空时,editing:item === currentItem返回false//则取消editing的作用this.currentItem = null;},//进入双击编辑状态dblc(item){//把当前点击的事件项赋值给currentItem数组this.currentItem = item;},clearItems(){//过滤所有未完成的事件项并赋值给新数组ithis.items = this.items.filter(function(i){return i.completed === false;})//ES6写法://this.items = this.items(i => !i.completed );},removeItem(index){//splice(index,1)从要删除的位置,每次删除一个this.items.splice(index,1);},addItem:function(e){   //ES6中 直接——> addItem(){}const content = e.target.value.trim();if (!content.length){return;}else{const id = this.items.length + 1;this.items.push({id:id,                   //ES6直接一个 id 即可content:content,        //ES6直接一个 content 即可completed:false          //完成状态默认false})}e.target.value = "";}},watch: {items:{deep:true,handler:function(newItems,oldItems){//将数据保存到本地localstorage.save(newItems)}}},})//window.onhashchange要在Vue实例对象之外window.onhashchange = function(){//截取从索引为2之后的hash字符串,若为空,则返回'all'const hash = window.location.hash.substr(2)||'all';//将hash赋值给上面Vue实例中的filterStatus//在Vue的计算属性中创建filter方法函数vm.filterStatus = hash;}//调用window.onhashchange();})(Vue);

Vue:TodoMVC实战相关推荐

  1. vue 动态添加class_前端开发:Vue项目实战-Music

    大家好,我来了,本期为大家带来的前端开发知识是"前端开发:Vue项目实战-Music",有兴趣做前端的朋友,和我一起来看看吧! 主要内容 项目环境搭建 路由导航实现 ListVie ...

  2. vue.js实战——购物车练习(包含全选功能)

    vue.js实战第5章 54页的练习1 直接放代码好了,全选的部分搞了好久,代码好像有点啰嗦,好在实现功能了(*^▽^*) HTML: <!DOCTYPE html> <html l ...

  3. js 查错_7年前端开发经验的我,写了本Vue.js实战开发,开源高清PDF下载

    Vue作为目前发展最迅速的前端框架越来越多的受到前端T程师青睐,Vue社区也是Web前端最活跃的社区之一. 更多的公司在转为Vue框架,但针对Vue优秀权威.实战的图书相对欠缺,梁灏著<Vue. ...

  4. vue --- vue.js实战基础篇课后练习

    练习1:在输入框聚焦时,增加对键盘上下键按键的支持,相当于加1和减1 练习2:增加一个控制步伐的prop-step,比如设置为10,点击加号按钮,一次增加10 思路: // 考虑到子模板的复用性,即在 ...

  5. axios vue 动态date_Web前端Vue系列之-Vue.js 实战

    课程简介: 课程目标:通过本课程的学习,让大家掌握Vue.js的进阶知识并在项目中应用. 适用人群:具有一定vue开发基础的开发人员. 课程概述:Vue (读音 /vjuː/,类似于 view) 是一 ...

  6. vue 将字符串最后一个字符给替换_前端开发:Vue项目实战-Music

    大家好,我来了,本期为大家带来的前端开发知识是"前端开发:Vue项目实战-Music",有兴趣做前端的朋友,和我一起来看看吧! 主要内容 项目环境搭建 路由导航实现 ListVie ...

  7. 【VUE项目实战】68、使用pm2管理项目

    接上篇<67.上线-开启gzip和配置HTTPS服务> 上一篇我们学习了如何开启gzip配置来减少文件访问体积,并配置HTTPS服务.本篇我们讲解一下如何使用pm2管理项目. 本篇是该系列 ...

  8. Vue项目实战 —— 后台管理系统( pc端 )

    前期回顾 我只写注释 -- 让Ai写代码_的博客-CSDN博客前期回顾 Vue项目实战 -- 哔哩哔哩移动端开发-- 第二篇_的博客-CSDN博客https://blog.csdn.net/m0_57 ...

  9. 3.Vue.js 实战 调查问卷WebApp项目

    问卷调查demo已上传,欢迎大家指正,欢迎大家下载:https://download.csdn.net/download/lzb348110175/11085995 如果您没积分的话,可以私信/评论, ...

  10. 《Vue.js实战》第七章.组件

    7.1 组件作用: 提高代码复用性,使项目易于维护 7.1 组件的使用 7.1.1 组件注册-全局注册 全局注册后,任何vue的实例都可以使用该组件. Vue.component('my-compon ...

最新文章

  1. 奥特linux系统监控,奥特曼知识大挑战答案
  2. SpringBoot异常处理-自定义HandlerExceptionResolver
  3. 《程序员面试宝典》笔记一
  4. 前端React结构工程-改写render
  5. flatten的用法
  6. 按不同vlan下发dhcp_Cisco三层交换上给不同Vlan配置不同的DHCP
  7. Open Xml Sdk创建目录
  8. 解决CentOS6.4 Docker “Couldn‘t connect to Docker daemon ...“ 问题
  9. LineBasedFrameDecoder 行解码器,回车换行符解决 TCP 粘包
  10. python 西门子wincc_西门子SIMATIC Manager和SIMATIC WinCC Explorer万能授权
  11. 大型连锁百货运维审计用什么软件好?有哪些功能?
  12. JAVA-输出一个三角形(详解)
  13. 64位win7下安装keras的过程
  14. ibm jazz_Jazz源代码管理管理指南
  15. codeforces div2 Not Assigning 题解
  16. 仿微信悬浮通知栏/横幅通知
  17. Error at hooking API “LoadStringA“ Dump first 32 bytes:
  18. IP54、IP67、IP6K7K、IP6K9K代表什么意思?
  19. 【钟表识别】基于计算机视觉实现钟表时间识别含Matlab源码
  20. 系统集成项目管理工程师教程考点精讲之风险的分类

热门文章

  1. 净资产/收益/本益比
  2. 【开源项目介绍】STC32基于u8g2库DMA驱动IIC or SPI OLED屏幕显示
  3. 我多久能变成一个傻瓜
  4. MacOS VSCode 常用快捷键配置
  5. JavaScript的三座大山--(1)--原型和原型链
  6. 无线城市如何创造下一个未来?
  7. mysql 数据类型 ppt_第4章__MySQL数据类型.ppt
  8. 让你闷声发大财的秘密,学会了一定能成为有钱人!
  9. MySQL排序规则导致无法命中索引问题
  10. 密歇根大学,一个被低估的美国公立常春藤名校