1、github拉取只含静态页面的项目
2、app.js文件
(function (Vue) {
const STORAGE_KEY='todo-items';
const itemStorage={
//获取数据
fetch:function(key){
return JSON.parse(localStorage.getItem(key)||'[]'); //json字符串转数组返回
},
//保存数据
save:function(key,items){
localStorage.setItem(key,JSON.stringify(items)); //json字符串
}
};
const items = [
{
id: 1,
content: "C#",
isCompleted: false
}, {
id: 2,
content: "Java",
isCompleted: false
}, {
id: 3,
content: "Python",
isCompleted: false
}];
//注册全局指令
Vue.directive('input-focus', {
inserted(el, binding) {
el.focus();
},
update(el, binding) {
if (binding.value) {
el.focus();
}
}
});
var vm = new Vue({
el: "#todoapp",
data: {
title: 'todos',
//items, //ES6对象属性的简写 items:items
items:itemStorage.fetch(STORAGE_KEY),
currentItem: null,
filterStatus: 'all'
},
//侦听器
watch:{
//items:function(newItems,oldItems){};//数组监听
//深度监听 监听数组中对象属性是否发生变化 deep:true
items:{
deep:true,
handler:function(newItems,oldItems){//处理函数
itemStorage.save(STORAGE_KEY,newItems);//监听到变化则保存到本地
}
}
},
//计算属性
computed: {
filterItems() {
switch (this.filterStatus) {
case 'active':
return this.items.filter(item => !item.isCompleted);
break;
case 'completed':
return this.items.filter(item => item.isCompleted);
break;
default:
return this.items;
break;
}
},
toggleAll: {
get() { //必须有返回值
return this.remaining === 0;
},
set(newStatus) {
this.items.forEach(item => item.isCompleted = newStatus);
}
},
//剩余未完成的数量
remaining() { //remaining:function()简写
//const unCompletedItems=this.items.filter(function(item){
// return !item.isCompleted;
//});
//return unCompletedItems.length;
return this.items.filter(item => !item.isCompleted).length;
},
itemOrItems() {
if (this.remaining === 1) {
return 'Item';
}
else {
return 'Items';
}
},
hasCompletedItem() {
return this.items.some(item => item.isCompleted);
}
},
methods: {
toEdit(item) {
this.currentItem = item;
},
cancleEdit() {
this.currentItem = null;
},
finishedEdit(item, index, event) {
const newContent = event.target.value;
if (!newContent) {
this.removeItem(index);
}
item.content = newContent;
this.currentItem = null;
},
addItem(event) {
const content = event.target.value.trim();
let maxId = Math.max.apply(Math, this.items.map(item => item.id));//对象组id最大值
if (content.length > 0) {
this.items.push(
{
id: maxId + 1,
content,
isCompleted: false
}
);
event.target.value = "";
}
},
removeItem(index) {
this.items.splice(index, 1);
},
clearAll() {
this.toggleAll = false;
},
addClass(e) {
let element = e.currentTarget;
if (!element.classList.contains('editing')) {
element.classList.toggle('editing');
}
}
}
});
//路由hash值改变时调用
window.onhashchange = () => {
const hash = window.location.hash.substr(2) || 'all';
vm.filterStatus = hash;
};
//刷新页面的时候调用
window.onhashchange();
})(Vue);
3、index.html页面vuejs绑定
<!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 v-on:keyup.enter="addItem" class="new-todo" placeholder="What needs to be done?" v-input-focus>
</header>
<!-- This section should be hidden by default and shown when there are todos -->
<template v-if="items.length">
<section class="main">
<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 filterItems" @dblclick="toEdit(item)"
v-bind:class="{completed:item.isCompleted,editing:item===currentItem}">
<div class="view">
<input class="toggle" type="checkbox" v-model="item.isCompleted">
<label>{{item.content}}</label>
<button class="destroy" v-bind:value="item.id" @click="removeItem(index)"></button>
</div>
<input v-input-focus="item===currentItem" class="edit" v-bind:value="item.content" @keyup.esc="cancleEdit"
@keyup.enter="finishedEdit(item,index,$event)" @blur="finishedEdit(item,index,$event)">
</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>{{remaining}}</strong> {{itemOrItems}} 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="hasCompletedItem" @click="clearAll" class="clear-completed">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/vue/dist/vue.js"></script>
<script src="node_modules/todomvc-common/base.js"></script>
<script src="js/app.js"></script>
</body>
</html>