案例介绍
需求说明
开始
# 下载模板到本地,重命名为 todomvc-vue
# --depth=1 表示只下载最后一次的 commit,其它历史记录不要,这样可以提高下载速度
git clone https://github.com/tastejs/todomvc-app-template.git todomvc-vue --depth=1
# 切换到 todomvc-vue 目录中,安装依赖项
cd todomvc-vue
npm install
# 打开 todomvc-vue 中的 index.html 预览模板
配置 browser-sync 浏览器同步测试工具
安装依赖
# 也可以 npm i -D browser-sync npm install --save-dev browser-sync
配置 scripts
"scripts": { "dev": "browser-sync start --server --files \"*.html, css/*.css, js/*.js\"", "start": "npm run dev" }
启动开发服务
# 或者 npm start npm run dev
主要实现一个日常任务的记录功能。
模板下载链接:https://github.com/tastejs/todomvc-app-template
业务代码:
index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>todos</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 id = "app" class="todoapp">
<header class="header">
<h1>{{ message }}</h1>
<input class="new-todo"
v-model="todoText"
@keydown.enter="handleNewTodo"
placeholder="What needs to be done?" autofocus>
</header>
<!-- This section should be hidden by default and shown when there are todos -->
<template v-if="todos.length">
<section class="main">
<input id="toggle-all" v-model="toggleAllStat" 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 -->
<!--当双击的时候,就把currentEditing = 当前双击的这个对象,对任务项 class 的 editing 都有一个判定:currentEditing===本身 则 edting 样式给本身-->
<li v-for="(item,index) in filterTodos" :class = "{completed:item.completed,editing:currentEditting === item}">
<div class="view">
<input class="toggle" type="checkbox" v-model = "item.completed">
<label @dblclick = "handleGetEdittingDblclick(item)">
{{ item.title }}
</label>
<button class="destroy" @click = "handleRemoveTodo(index)"></button>
</div>
<!--由于有双击取消不保存的功能(v-bind),这里不使用双向数据绑定(v-model)-->
<input class="edit" :value="item.title"
@keydown.enter = "handleSaveEdit(item,index,$event)"
@blur = "handleSaveEdit(item,index,$event)"
@keydown.esc = "handleCanceEditEsc">
</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>{{ remainingCount }}</strong> item left</span>
<!-- Remove this if you don't implement routing -->
<ul class="filters">
<li>
<a :class="{selected:filterText === ''}" href="#/">All</a>
</li>
<li>
<a :class="{selected:filterText === 'active'}" href="#/active">Active</a>
</li>
<li>
<a :class="{selected:filterText === 'completed'}" href="#/completed">Completed</a>
</li>
</ul>
<!-- Hidden if no completed items are left ↓ -->
<!--every方法,条件中 item.completed 全部为true,则返回 true
some 方法,条件中 item.completed 只要有一个 true 则为true,必须所有的为 false 才为false-->
<button v-if="todos.some(item => item.completed)" class="clear-completed" @click = "handleClearCompleted">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 = "https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<script src="js/app.js"></script>
</body>
</html>
app.js
;(function(){
// const todos = [
// {
// id:1,
// title:'吃饭',
// completed:true
// },
// {
// id:2,
// title:'睡觉',
// completed:false
// },
// {
// id:3,
// title:'写代码',
// completed:true
// },
// ]
window.app = new Vue({
data:{
message:'todos_lzm1',
todos:JSON.parse(window.localStorage.getItem('todos') || '[]'),
todoText:'',
currentEditting:null,
filterText:'all'
},
//计算属性是vue的一大特色
//一种带有行为的属性,本质是方法,本质是方法但是不能当做方法来调用,必须当做属性来使用
//好处:相比方法的优势是会缓存计算的结果,效率很高
//计算属性不是方法,只能当做属性来使用
//
computed:{
//该成员就是一个方法,但是在使用的时候不能调用,
//简写方式,
// remainingCount(){
// return this.todos.filter(t => !t.completed).length
// }
//完整写法
remainingCount:{
//当你访问 remainingCount 时会默认调用 get() 方法
get(){
return this.todos.filter(t => !t.completed).length
},
//当你 实例.remainingCount = xxx 的时候会自动调用 set 方法
// set(){
// }
},
toggleAllStat:{
get(){
//计算属性知道他依赖todos
//当 todos 发生改变的时候,计算属性也会发生变化
return this.todos.every(t => t.completed)
},
set(){
//在自己的方法中调用自己就是访问自己的 get 方法
const checked = !this.toggleAllStat
this.todos.forEach(item => {
item.completed = checked
})
}
},
filterTodos(){
switch(this.filterText){
case 'active':
return this.todos.filter(t => !t.completed)
break
case 'completed':
return this.todos.filter(t => t.completed)
break
default:
return this.todos
break
}
}
},
//监视成员的改变
watch:{
//当监视到 todos 改变时会调用 handler 方法
todos:{
handler(val,oldval){
//监视到 todos 的变化,把 todos 本次存储记录数据的状态
window.localStorage.setItem('todos',JSON.stringify(this.todos))
},
deep:true //深度监视,只有这样才能监视到 todos 孩子...孩子...的变化
}
},
methods:{
handleNewTodo(){//添加新的要完成的任务
//console.log('111')
const todoText = this.todoText.trim()
if(!todoText.length){//非空判断
return
}
const todos = this.todos
todos.push({//将要添加的内容push到todos中
//如果数据元素为空就给1,否则就是最后一个元素的 id + 1
id:todos.length ? todos[todos.length-1].id+1:1,
title:todoText,
completed:false
})
this.todoText=''//添加完后清空
},
// handleToggleAll(e){//选中和取消所有任务项
// //0.绑定 checkbox 的 change 事件
// //1.获取 CheckBox 的选中状态
// //2.循环所有子任务项的选中状态,将 checkbox 的选中状态赋值给它们
// const checked = e.target.checked
// this.todos.forEach(item => {
// item.completed = checked
// })
// },
//当事件函数没有传参数的时候,第一个参数就是默认的事件原对象:event
//当事件函数有参数的时候,就没有办法获取事件原函数:event
//可在传递参数的时候手动在调用方法的时候 $event 来接受 event 事件对象
handleRemoveTodo(index){//删除单个任务项
//console.log(index)
this.todos.splice(index,1)
},
handleGetEdittingDblclick(todo){//双击获取编辑框
console.log(todo)
this.currentEditting = todo
},
//保存编辑任务,敲回车保存
handleSaveEdit(todo,index,e){
//0.注册绑定事件处理函数
//1.获取文本框的数据
//2.数据校验
// 如果数据为空,则删除文本框
// 否则保存编辑
//console.log(e.target.value)
const value = e.target.value
if(!value.length){//若为空,则删除
this.todos.splice(index,1)
}else{ //保存
todo.title = value
this.currentEditting = null//取消样式
}
},
//按下esc键取消编辑,不保存
handleCanceEditEsc(){
//取消样式即可
this.currentEditting = null
},
handleClearCompleted(){ //点击clear completed清除已完成的选项
//注意不要在foreach中删除元素,可以使用for循环,每删一个可以手动的改变索引
for (var i = 0; i < this.todos.length; i++) {
if(this.todos[i].completed){
this.todos.splice(i,1)
i--
}
}
},
//获取剩余未完成任务数量
// getRemainCount(){
// return this.todos.filter(t => !t.completed).length
// }
}
}).$mount('#app')
//该事件只有 change 的时候才会执行,页面初始化的时候不会执行
//注册 hash(锚点) 的改变事件
window.onhashchange = function(){
app.filterText = window.location.hash.substr(2)
}
//页面初始化的时候调用一次,保持路由状态
window.onhashchange()
})()
效果展示: