有沒有做網(wǎng)站的教程網(wǎng)站收錄查詢代碼
目錄
1 拿到一個功能模塊首先需要拆分組件:
2 使用組件實現(xiàn)靜態(tài)頁面的效果
3 分析數(shù)據(jù)保存在哪個組件
4 實現(xiàn)添加數(shù)據(jù)
5 實現(xiàn)復(fù)選框勾選
6 實現(xiàn)數(shù)據(jù)的刪除
7 實現(xiàn)底部組件中數(shù)據(jù)的統(tǒng)計
8 實現(xiàn)勾選全部的小復(fù)選框來實現(xiàn)大復(fù)選框的勾選
9 實現(xiàn)勾選大復(fù)選框來實現(xiàn)所有的小復(fù)選框都被勾選
10 清空所有數(shù)據(jù)
11 實現(xiàn)案例中的數(shù)據(jù)存入本地存儲
12 案例中使用自定義事件完成組件間的數(shù)據(jù)通信
13 案例中實現(xiàn)數(shù)據(jù)的編輯
14 實現(xiàn)數(shù)據(jù)進出的動畫效果
【分析】組件化編碼的流程
1. 實現(xiàn)靜態(tài)組件:抽取組件,使用組件實現(xiàn)靜態(tài)頁面效果
2.展示動態(tài)數(shù)據(jù):
? ? ? ? 2.1 數(shù)據(jù)的類型、名稱是什么?
? ? ? ? 2.2 數(shù)據(jù)保存在哪個組件?
3.交互---從綁定事件監(jiān)聽開始
1 拿到一個功能模塊首先需要拆分組件:
2 使用組件實現(xiàn)靜態(tài)頁面的效果
【main.js】
import Vue from 'vue'
import App from './App.vue'Vue.config.productionTip = falsenew Vue({el: '#app',render: h => h(App)
})
【MyHeader】
<template><div class="todo-header"><input type="text"/></div>
</template><script>export default {name: 'MyHeader',}
</script><style scoped>/*header*/.todo-header input {width: 560px;height: 28px;font-size: 14px;border: 1px solid #ccc;border-radius: 4px;padding: 4px 7px;}.todo-header input:focus {outline: none;border-color: rgba(82, 168, 236, 0.8);box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);}
</style>
【Item】
<template> <li><label><input type="checkbox"/><span v-for="todo in todos" :key="todo.id">{{todo.title}}</span></label><button class="btn btn-danger">刪除</button></li>
</template><script>export default {name: 'Item',data() {return {todos: [{id: '001', title: '吃飯', done: true},{id: '002', title: '學(xué)習(xí)', done: false},{id: '003', title: '追劇', done: true},]}},}
</script><style scoped>/*item*/li {list-style: none;height: 36px;line-height: 36px;padding: 0 5px;border-bottom: 1px solid #ddd;}li label {float: left;cursor: pointer;}li label li input {vertical-align: middle;margin-right: 6px;position: relative;top: -1px;}li button {float: right;display: none;margin-top: 3px;}li:before {content: initial;}li:last-child {border-bottom: none;}li:hover {background-color: rgb(196, 195, 195);}li:hover button{display: block;}
</style>
【List】
<template><ul class="todo-main"><Item></Item><Item></Item></ul>
</template><script>import Item from './Item.vue'export default {name: 'List',components:{Item},}
</script><style scoped>/*main*/.todo-main {margin-left: 0px;border: 1px solid #ddd;border-radius: 2px;padding: 0px;}.todo-empty {height: 40px;line-height: 40px;border: 1px solid #ddd;border-radius: 2px;padding-left: 5px;margin-top: 10px;}
</style>
【MyFooter】
<template><div class="todo-footer"><label><input type="checkbox"/></label><span><span>已完成 0</span> / 3</span><button class="btn btn-danger">清除已完成任務(wù)</button></div>
</template><script>export default {name: 'MyFooter',}
</script><style scoped>/*footer*/.todo-footer {height: 40px;line-height: 40px;padding-left: 6px;margin-top: 5px;}.todo-footer label {display: inline-block;margin-right: 20px;cursor: pointer;}.todo-footer label input {position: relative;top: -1px;vertical-align: middle;margin-right: 5px;}.todo-footer button {float: right;margin-top: 5px;}
</style>
【App】?
<template><div id="root"><div class="todo-container"><div class="todo-wrap"><MyHeader></MyHeader><List></List><MyFooter></MyFooter></div></div></div>
</template><script>import MyHeader from './components/MyHeader.vue'import List from './components/List.vue'import MyFooter from './components/MyFooter.vue'export default {name:'App',components:{MyHeader,List,MyFooter} }
</script><style>/*base*/body {background: #fff;}.btn {display: inline-block;padding: 4px 12px;margin-bottom: 0;font-size: 14px;line-height: 20px;text-align: center;vertical-align: middle;cursor: pointer;box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);border-radius: 4px;}.btn-danger {color: #fff;background-color: #da4f49;border: 1px solid #bd362f;}.btn-danger:hover {color: #fff;background-color: #bd362f;}.btn:focus {outline: none;}.todo-container {width: 600px;margin: 0 auto;}.todo-container .todo-wrap {padding: 10px;border: 1px solid #ddd;border-radius: 5px;}
</style>
通過以上代碼就可以實現(xiàn)靜態(tài)頁面的效果了!!!
3 分析數(shù)據(jù)保存在哪個組件
在上述代碼中數(shù)據(jù)是保存在Item組件中的,但是如果想要在后續(xù)實現(xiàn)一系列交互效果:在MyHeader組件中需要添加數(shù)據(jù),而MyHeader組件和Item組件沒有直接的關(guān)系, 就當前學(xué)習(xí)階段的知識而言,并不能實現(xiàn)這兩個組件之間的通信(后續(xù)會有解決方案),同理MyFooter也一樣。
【分析】因為App組件是所有組件的父組件,所以數(shù)據(jù)放在App組件中,再使用props配置,所有的子組件就都可以訪問到。
【App】(同時需要將數(shù)據(jù)傳遞到Item組件中,在當前階段只能通過props配置一層一層往下傳,所以是 App-->List,List-->Item)
1. 實現(xiàn)?App-->List?傳遞todos數(shù)據(jù)
<template><div id="root"><div class="todo-container"><div class="todo-wrap"><MyHeader></MyHeader><List :todos="todos"></List><MyFooter></MyFooter></div></div></div>
</template><script>import MyHeader from './components/MyHeader.vue'import List from './components/List.vue'import MyFooter from './components/MyFooter.vue'export default {name:'App',components:{MyHeader,List,MyFooter},data() {return {todos: [{id: '001', title: '吃飯', done: true},{id: '002', title: '學(xué)習(xí)', done: false},{id: '003', title: '追劇', done: true},]}},}
</script><style>/*base*/body {background: #fff;}.btn {display: inline-block;padding: 4px 12px;margin-bottom: 0;font-size: 14px;line-height: 20px;text-align: center;vertical-align: middle;cursor: pointer;box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);border-radius: 4px;}.btn-danger {color: #fff;background-color: #da4f49;border: 1px solid #bd362f;}.btn-danger:hover {color: #fff;background-color: #bd362f;}.btn:focus {outline: none;}.todo-container {width: 600px;margin: 0 auto;}.todo-container .todo-wrap {padding: 10px;border: 1px solid #ddd;border-radius: 5px;}
</style>
2. List接受todos數(shù)據(jù)
<template><ul class="todo-main"><Item v-for="todo in todos" :key="todo.id"></Item></ul>
</template><script>import Item from './Item.vue'export default {name: 'List',components:{Item},props: ['todos']}
</script><style scoped>/*main*/.todo-main {margin-left: 0px;border: 1px solid #ddd;border-radius: 2px;padding: 0px;}.todo-empty {height: 40px;line-height: 40px;border: 1px solid #ddd;border-radius: 2px;padding-left: 5px;margin-top: 10px;}
</style>
通過上述的代碼便可以根據(jù)數(shù)據(jù)的數(shù)量來渲染出幾個Item了,但是此時Item里面是沒有內(nèi)容的,所以需要?List-->Item 再次傳遞每條數(shù)據(jù)
3. 實現(xiàn)?List-->Item?傳遞todo數(shù)據(jù)
<template><ul class="todo-main"><Item v-for="todo in todos" :key="todo.id" :todo="todo"></Item></ul>
</template><script>import Item from './Item.vue'export default {name: 'List',components:{Item},props: ['todos']}
</script><style scoped>/*main*/.todo-main {margin-left: 0px;border: 1px solid #ddd;border-radius: 2px;padding: 0px;}.todo-empty {height: 40px;line-height: 40px;border: 1px solid #ddd;border-radius: 2px;padding-left: 5px;margin-top: 10px;}
</style>
4. Item接受todo數(shù)據(jù)
<template> <li><label><input type="checkbox"/><span>{{todo.title}}</span></label><button class="btn btn-danger">刪除</button></li>
</template><script>export default {name: 'Item',// 聲明接收todo對象props:['todo'],}
</script><style scoped>/*item*/li {list-style: none;height: 36px;line-height: 36px;padding: 0 5px;border-bottom: 1px solid #ddd;}li label {float: left;cursor: pointer;}li label li input {vertical-align: middle;margin-right: 6px;position: relative;top: -1px;}li button {float: right;display: none;margin-top: 3px;}li:before {content: initial;}li:last-child {border-bottom: none;}li:hover {background-color: rgb(196, 195, 195);}li:hover button{display: block;}
</style>
4 實現(xiàn)添加數(shù)據(jù)
【App】定義接收數(shù)據(jù)的回調(diào)函數(shù)
<template><div id="root"><div class="todo-container"><div class="todo-wrap"><MyHeader :addTodo="addTodo"></MyHeader><List :todos="todos"></List><MyFooter></MyFooter></div></div></div>
</template><script>import MyHeader from './components/MyHeader.vue'import List from './components/List.vue'import MyFooter from './components/MyFooter.vue'export default {name:'App',components:{MyHeader,List,MyFooter},data() {return {todos: [{id: '001', title: '吃飯', done: true},{id: '002', title: '學(xué)習(xí)', done: false},{id: '003', title: '追劇', done: true},]}},methods: {// 添加一個todoaddTodo(todoObj) {this.todos.unshift(todoObj)}}}
</script><style>/*base*/body {background: #fff;}.btn {display: inline-block;padding: 4px 12px;margin-bottom: 0;font-size: 14px;line-height: 20px;text-align: center;vertical-align: middle;cursor: pointer;box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);border-radius: 4px;}.btn-danger {color: #fff;background-color: #da4f49;border: 1px solid #bd362f;}.btn-danger:hover {color: #fff;background-color: #bd362f;}.btn:focus {outline: none;}.todo-container {width: 600px;margin: 0 auto;}.todo-container .todo-wrap {padding: 10px;border: 1px solid #ddd;border-radius: 5px;}
</style>
【MyHeader】實現(xiàn)添加數(shù)據(jù)的方法
<template><div class="todo-header"><!-- 綁定鍵盤回車事件 --><input type="text" placeholder="請輸入你的任務(wù)名稱,按回車鍵確認" @keyup.enter="add" v-model="title"/></div>
</template><script>import {nanoid} from 'nanoid' // 生成idexport default {name: 'MyHeader',data() {return {title: ''}},props: ['addTodo'], // 接收父組件傳過來的addTodo函數(shù)methods: {add(e) {// 校驗數(shù)據(jù)if (!this.title.trim()) return alert('輸入不能為空')// 將用戶的輸入包裝成為一個todo對象const todoObj = {id: nanoid(),/* title: e.target.value, */title: this.title,done: false}console.log(todoObj)// console.log(e.target.value)// console.log(this.title)// 通知App組件去添加一個todo對象this.addTodo(todoObj)// 清空輸入this.title = ''}}}
</script><style scoped>/*header*/.todo-header input {width: 560px;height: 28px;font-size: 14px;border: 1px solid #ccc;border-radius: 4px;padding: 4px 7px;}.todo-header input:focus {outline: none;border-color: rgba(82, 168, 236, 0.8);box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);}
</style>
5 實現(xiàn)復(fù)選框勾選
【App】也是要逐層傳遞
<template><div id="root"><div class="todo-container"><div class="todo-wrap"><MyHeader :addTodo="addTodo"></MyHeader><List :todos="todos" :changeTodo="changeTodo"></List><MyFooter></MyFooter></div></div></div>
</template><script>import MyHeader from './components/MyHeader.vue'import List from './components/List.vue'import MyFooter from './components/MyFooter.vue'export default {name:'App',components:{MyHeader,List,MyFooter},data() {return {todos: [{id: '001', title: '吃飯', done: true},{id: '002', title: '學(xué)習(xí)', done: false},{id: '003', title: '追劇', done: true},]}},methods: {// 添加一個todoaddTodo(todoObj) {this.todos.unshift(todoObj)},// 勾選或者取消勾選一個todochangeTodo(id) {this.todos.forEach((todo) => {if (todo.id === id) todo.done = !todo.done})},}}
</script><style>/*base*/body {background: #fff;}.btn {display: inline-block;padding: 4px 12px;margin-bottom: 0;font-size: 14px;line-height: 20px;text-align: center;vertical-align: middle;cursor: pointer;box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);border-radius: 4px;}.btn-danger {color: #fff;background-color: #da4f49;border: 1px solid #bd362f;}.btn-danger:hover {color: #fff;background-color: #bd362f;}.btn:focus {outline: none;}.todo-container {width: 600px;margin: 0 auto;}.todo-container .todo-wrap {padding: 10px;border: 1px solid #ddd;border-radius: 5px;}
</style>
【List】
<template><ul class="todo-main"><Item v-for="todo in todos" :key="todo.id" :todo="todo":changeTodo="changeTodo"></Item></ul>
</template><script>import Item from './Item.vue'export default {name: 'List',components:{Item},props: ['todos', 'changeTodo']}
</script><style scoped>/*main*/.todo-main {margin-left: 0px;border: 1px solid #ddd;border-radius: 2px;padding: 0px;}.todo-empty {height: 40px;line-height: 40px;border: 1px solid #ddd;border-radius: 2px;padding-left: 5px;margin-top: 10px;}
</style>
【Item】
<template> <li><label><input type="checkbox" :checked="todo.done" @change="handleCheck(todo.id)"/><span>{{todo.title}}</span></label><button class="btn btn-danger">刪除</button></li>
</template><script>export default {name: 'Item',// 聲明接收todo對象props:['todo', 'changeTodo'],methods: {// 勾選 or 取消勾選handleCheck(id) {// 通知 App組件將對應(yīng)的todo對象的狀態(tài)改變this.changeTodo(id)}}}
</script><style scoped>/*item*/li {list-style: none;height: 36px;line-height: 36px;padding: 0 5px;border-bottom: 1px solid #ddd;}li label {float: left;cursor: pointer;}li label li input {vertical-align: middle;margin-right: 6px;position: relative;top: -1px;}li button {float: right;display: none;margin-top: 3px;}li:before {content: initial;}li:last-child {border-bottom: none;}li:hover {background-color: rgb(196, 195, 195);}li:hover button{display: block;}
</style>
6 實現(xiàn)數(shù)據(jù)的刪除
【App】
<template><div id="root"><div class="todo-container"><div class="todo-wrap"><MyHeader :addTodo="addTodo"></MyHeader><List :todos="todos" :changeTodo="changeTodo" :deleteTodo="deleteTodo"></List><MyFooter></MyFooter></div></div></div>
</template><script>import MyHeader from './components/MyHeader.vue'import List from './components/List.vue'import MyFooter from './components/MyFooter.vue'export default {name:'App',components:{MyHeader,List,MyFooter},data() {return {todos: [{id: '001', title: '吃飯', done: true},{id: '002', title: '學(xué)習(xí)', done: false},{id: '003', title: '追劇', done: true},]}},methods: {// 添加一個todoaddTodo(todoObj) {this.todos.unshift(todoObj)},// 勾選或者取消勾選一個todochangeTodo(id) {this.todos.forEach((todo) => {if (todo.id === id) todo.done = !todo.done})},// 刪除一個tododeleteTodo(id) {this.todos = this.todos.filter((todo) => todo.id !== id)},}}
</script><style>/*base*/body {background: #fff;}.btn {display: inline-block;padding: 4px 12px;margin-bottom: 0;font-size: 14px;line-height: 20px;text-align: center;vertical-align: middle;cursor: pointer;box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);border-radius: 4px;}.btn-danger {color: #fff;background-color: #da4f49;border: 1px solid #bd362f;}.btn-danger:hover {color: #fff;background-color: #bd362f;}.btn:focus {outline: none;}.todo-container {width: 600px;margin: 0 auto;}.todo-container .todo-wrap {padding: 10px;border: 1px solid #ddd;border-radius: 5px;}
</style>
【List】
<template><ul class="todo-main"><Item v-for="todo in todos" :key="todo.id" :todo="todo":changeTodo="changeTodo":deleteTodo="deleteTodo"></Item></ul>
</template><script>import Item from './Item.vue'export default {name: 'List',components:{Item},props: ['todos', 'changeTodo', 'deleteTodo']}
</script><style scoped>/*main*/.todo-main {margin-left: 0px;border: 1px solid #ddd;border-radius: 2px;padding: 0px;}.todo-empty {height: 40px;line-height: 40px;border: 1px solid #ddd;border-radius: 2px;padding-left: 5px;margin-top: 10px;}
</style>
【Item】
<template> <li><label><input type="checkbox" :checked="todo.done" @change="handleCheck(todo.id)"/><span>{{todo.title}}</span></label><button class="btn btn-danger" @click="handleDelete(todo.id)">刪除</button></li>
</template><script>export default {name: 'Item',// 聲明接收todo對象props:['todo', 'changeTodo', 'deleteTodo'],methods: {// 勾選 or 取消勾選handleCheck(id) {// 通知 App組件將對應(yīng)的todo對象的狀態(tài)改變this.changeTodo(id)},// 刪除操作handleDelete(id) {// console.log(id)if (confirm("確定刪除嗎?")) {// 通知App刪除this.deleteTodo(id)}}}}
</script><style scoped>/*item*/li {list-style: none;height: 36px;line-height: 36px;padding: 0 5px;border-bottom: 1px solid #ddd;}li label {float: left;cursor: pointer;}li label li input {vertical-align: middle;margin-right: 6px;position: relative;top: -1px;}li button {float: right;display: none;margin-top: 3px;}li:before {content: initial;}li:last-child {border-bottom: none;}li:hover {background-color: rgb(196, 195, 195);}li:hover button{display: block;}
</style>
7 實現(xiàn)底部組件中數(shù)據(jù)的統(tǒng)計
【分析】如果想要統(tǒng)計數(shù)據(jù)的數(shù)量,就需要將數(shù)據(jù)傳遞到MyFooter組件中
【App】
<template><div id="root"><div class="todo-container"><div class="todo-wrap"><MyHeader :addTodo="addTodo"></MyHeader><List :todos="todos" :changeTodo="changeTodo" :deleteTodo="deleteTodo"></List><MyFooter:todos="todos"></MyFooter></div></div></div>
</template><script>import MyHeader from './components/MyHeader.vue'import List from './components/List.vue'import MyFooter from './components/MyFooter.vue'export default {name:'App',components:{MyHeader,List,MyFooter},data() {return {todos: [{id: '001', title: '吃飯', done: true},{id: '002', title: '學(xué)習(xí)', done: false},{id: '003', title: '追劇', done: true},]}},methods: {// 添加一個todoaddTodo(todoObj) {this.todos.unshift(todoObj)},// 勾選或者取消勾選一個todochangeTodo(id) {this.todos.forEach((todo) => {if (todo.id === id) todo.done = !todo.done})},// 刪除一個tododeleteTodo(id) {this.todos = this.todos.filter((todo) => todo.id !== id)},}}
</script><style>/*base*/body {background: #fff;}.btn {display: inline-block;padding: 4px 12px;margin-bottom: 0;font-size: 14px;line-height: 20px;text-align: center;vertical-align: middle;cursor: pointer;box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);border-radius: 4px;}.btn-danger {color: #fff;background-color: #da4f49;border: 1px solid #bd362f;}.btn-danger:hover {color: #fff;background-color: #bd362f;}.btn:focus {outline: none;}.todo-container {width: 600px;margin: 0 auto;}.todo-container .todo-wrap {padding: 10px;border: 1px solid #ddd;border-radius: 5px;}
</style>
【MyFooter】
在這個組件中可以使用計算屬性實現(xiàn)數(shù)據(jù)的總長度和被勾選的數(shù)據(jù)的計算
<template><div class="todo-footer" v-if="todosLength"><label><input type="checkbox"/></label><span><span>已完成 {{doneTotal}}</span> / {{todosLength}}</span><button class="btn btn-danger">清除已完成任務(wù)</button></div>
</template><script>
export default {name: 'MyFooter',props: ['todos'],computed: {todosLength() {return this.todos.length},doneTotal() {return this.todos.filter(todo => todo.done).length// 也可以使用下面求和來實現(xiàn)// return this.todos.reduce((pre, todo) => pre + (todo.done ? 1 : 0), 0)}}
}
</script><style scoped>/*footer*/.todo-footer {height: 40px;line-height: 40px;padding-left: 6px;margin-top: 5px;}.todo-footer label {display: inline-block;margin-right: 20px;cursor: pointer;}.todo-footer label input {position: relative;top: -1px;vertical-align: middle;margin-right: 5px;}.todo-footer button {float: right;margin-top: 5px;}
</style>
8 實現(xiàn)勾選全部的小復(fù)選框來實現(xiàn)大復(fù)選框的勾選
:checked="isAll"
isAll也是通過計算屬性計算得來的
【MyFooter】?
<template><div class="todo-footer" v-if="todosLength"><label><input type="checkbox" :checked="isAll"/></label><span><span>已完成 {{doneTotal}}</span> / {{todosLength}}</span><button class="btn btn-danger">清除已完成任務(wù)</button></div>
</template><script>
export default {name: 'MyFooter',props: ['todos'],computed: {todosLength() {return this.todos.length},doneTotal() {return this.todos.filter(todo => todo.done).length// 也可以使用下面求和來實現(xiàn)// return this.todos.reduce((pre, todo) => pre + (todo.done ? 1 : 0), 0)},isAll() {return this.doneTotal === this.todosLength && this.todosLength > 0}, }
}
</script><style scoped>/*footer*/.todo-footer {height: 40px;line-height: 40px;padding-left: 6px;margin-top: 5px;}.todo-footer label {display: inline-block;margin-right: 20px;cursor: pointer;}.todo-footer label input {position: relative;top: -1px;vertical-align: middle;margin-right: 5px;}.todo-footer button {float: right;margin-top: 5px;}
</style>
9 實現(xiàn)勾選大復(fù)選框來實現(xiàn)所有的小復(fù)選框都被勾選
【App】
<template><div id="root"><div class="todo-container"><div class="todo-wrap"><MyHeader :addTodo="addTodo"></MyHeader><List :todos="todos" :changeTodo="changeTodo" :deleteTodo="deleteTodo"></List><MyFooter:todos="todos":checkAllTodo="checkAllTodo"></MyFooter></div></div></div>
</template><script>import MyHeader from './components/MyHeader.vue'import List from './components/List.vue'import MyFooter from './components/MyFooter.vue'export default {name:'App',components:{MyHeader,List,MyFooter},data() {return {todos: [{id: '001', title: '吃飯', done: true},{id: '002', title: '學(xué)習(xí)', done: false},{id: '003', title: '追劇', done: true},]}},methods: {// 添加一個todoaddTodo(todoObj) {this.todos.unshift(todoObj)},// 勾選或者取消勾選一個todochangeTodo(id) {this.todos.forEach((todo) => {if (todo.id === id) todo.done = !todo.done})},// 刪除一個tododeleteTodo(id) {this.todos = this.todos.filter((todo) => todo.id !== id)},// 全選or取消全選checkAllTodo(done) {this.todos.forEach((todo) => {todo.done = done})},}}
</script><style>/*base*/body {background: #fff;}.btn {display: inline-block;padding: 4px 12px;margin-bottom: 0;font-size: 14px;line-height: 20px;text-align: center;vertical-align: middle;cursor: pointer;box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);border-radius: 4px;}.btn-danger {color: #fff;background-color: #da4f49;border: 1px solid #bd362f;}.btn-danger:hover {color: #fff;background-color: #bd362f;}.btn:focus {outline: none;}.todo-container {width: 600px;margin: 0 auto;}.todo-container .todo-wrap {padding: 10px;border: 1px solid #ddd;border-radius: 5px;}
</style>
【MyFooter】
<template><div class="todo-footer" v-if="todosLength"><label><input type="checkbox" :checked="isAll" @change="checkAll"/></label><span><span>已完成 {{doneTotal}}</span> / {{todosLength}}</span><button class="btn btn-danger">清除已完成任務(wù)</button></div>
</template><script>
export default {name: 'MyFooter',props: ['todos', 'checkAllTodo'],computed: {todosLength() {return this.todos.length},doneTotal() {return this.todos.filter(todo => todo.done).length// 也可以使用下面求和來實現(xiàn)// return this.todos.reduce((pre, todo) => pre + (todo.done ? 1 : 0), 0)},isAll() {return this.doneTotal === this.todosLength && this.todosLength > 0}, },methods: {checkAll(e) {this.checkAllTodo(e.target.checked)}}
}
</script><style scoped>/*footer*/.todo-footer {height: 40px;line-height: 40px;padding-left: 6px;margin-top: 5px;}.todo-footer label {display: inline-block;margin-right: 20px;cursor: pointer;}.todo-footer label input {position: relative;top: -1px;vertical-align: middle;margin-right: 5px;}.todo-footer button {float: right;margin-top: 5px;}
</style>
10 清空所有數(shù)據(jù)
【App】
<template><div id="root"><div class="todo-container"><div class="todo-wrap"><MyHeader :addTodo="addTodo"></MyHeader><List :todos="todos" :changeTodo="changeTodo" :deleteTodo="deleteTodo"></List><MyFooter:todos="todos":checkAllTodo="checkAllTodo":clearAllTodo="clearAllTodo"></MyFooter></div></div></div>
</template><script>import MyHeader from './components/MyHeader.vue'import List from './components/List.vue'import MyFooter from './components/MyFooter.vue'export default {name:'App',components:{MyHeader,List,MyFooter},data() {return {todos: [{id: '001', title: '吃飯', done: true},{id: '002', title: '學(xué)習(xí)', done: false},{id: '003', title: '追劇', done: true},]}},methods: {// 添加一個todoaddTodo(todoObj) {this.todos.unshift(todoObj)},// 勾選或者取消勾選一個todochangeTodo(id) {this.todos.forEach((todo) => {if (todo.id === id) todo.done = !todo.done})},// 刪除一個tododeleteTodo(id) {this.todos = this.todos.filter((todo) => todo.id !== id)},// 全選or取消全選checkAllTodo(done) {this.todos.forEach((todo) => {todo.done = done})},// 清空所有已經(jīng)完成的todoclearAllTodo() {this.todos = this.todos.filter((todo) => !todo.done)}}}
</script><style>/*base*/body {background: #fff;}.btn {display: inline-block;padding: 4px 12px;margin-bottom: 0;font-size: 14px;line-height: 20px;text-align: center;vertical-align: middle;cursor: pointer;box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);border-radius: 4px;}.btn-danger {color: #fff;background-color: #da4f49;border: 1px solid #bd362f;}.btn-danger:hover {color: #fff;background-color: #bd362f;}.btn:focus {outline: none;}.todo-container {width: 600px;margin: 0 auto;}.todo-container .todo-wrap {padding: 10px;border: 1px solid #ddd;border-radius: 5px;}
</style>
【MyFooter】
<template><div class="todo-footer" v-if="todosLength"><label><input type="checkbox" :checked="isAll" @change="checkAll"/></label><span><span>已完成 {{doneTotal}}</span> / {{todosLength}}</span><button class="btn btn-danger" @click="clearTodo">清除已完成任務(wù)</button></div>
</template><script>
export default {name: 'MyFooter',props: ['todos', 'checkAllTodo', 'clearAllTodo'],computed: {todosLength() {return this.todos.length},doneTotal() {return this.todos.filter(todo => todo.done).length// 也可以使用下面求和來實現(xiàn)// return this.todos.reduce((pre, todo) => pre + (todo.done ? 1 : 0), 0)},isAll() {return this.doneTotal === this.todosLength && this.todosLength > 0}, },methods: {checkAll(e) {this.checkAllTodo(e.target.checked)},clearTodo() {this.clearAllTodo()}}
}
</script><style scoped>/*footer*/.todo-footer {height: 40px;line-height: 40px;padding-left: 6px;margin-top: 5px;}.todo-footer label {display: inline-block;margin-right: 20px;cursor: pointer;}.todo-footer label input {position: relative;top: -1px;vertical-align: middle;margin-right: 5px;}.todo-footer button {float: right;margin-top: 5px;}
</style>
11 實現(xiàn)案例中的數(shù)據(jù)存入本地存儲
【分析】首先我們要知道什么時候需要將數(shù)據(jù)存入本地存儲?所以這就用到了watch監(jiān)聽,當todos的值發(fā)生變化時,將新的值存入本地存儲。
又因為當我們勾選復(fù)選框時,我們發(fā)現(xiàn)本地存儲中的 done 值并沒有發(fā)生變化?這主要是因為監(jiān)聽默認只會監(jiān)聽第一層,如果想要監(jiān)聽對象中某個數(shù)據(jù)發(fā)生變化時,就需要深度監(jiān)視了。
【App】
這里使用 || 運算可以防止一開始本地存儲中沒有數(shù)據(jù)而報錯
12 案例中使用自定義事件完成組件間的數(shù)據(jù)通信
這邊以添加數(shù)據(jù)為例
【App】
給發(fā)送數(shù)據(jù)的組件綁定自定義事件
<MyHeader @addTodo="addTodo"></MyHeader>...methods: {// 添加一個todoaddTodo(todoObj) {this.todos.unshift(todoObj)},
}
【MyHeader】
13 案例中實現(xiàn)數(shù)據(jù)的編輯
需求分析:當點擊編輯按鈕時,變成input表單修改數(shù)據(jù),此時編輯按鈕隱藏,當失去焦點時,編輯完成,顯示編輯后的數(shù)據(jù),同時編輯按鈕顯示。
?這邊使用全局事件總線來實現(xiàn)通信
【App】
methods: {...// 更改updateTodo(id,title) {this.todos.forEach((todo) => {if (todo.id === id) todo.title = title})},...},mounted() {this.$bus.$on('updateTodo', this.updateTodo)},beforeDestroy() {this.$bus.$off('updateTodo')}
【Item】
因為如果想要失去焦點時實現(xiàn)數(shù)據(jù)的修改,那么你必須提前獲取焦點,但是由于Vue的執(zhí)行機制,當Vue底層監(jiān)視到數(shù)據(jù)發(fā)生改變時,它并不會立即去重新渲染模板,而是繼續(xù)執(zhí)行后面的代碼,所以如果不加以處理的話,直接獲取焦點,肯定會報錯,因為頁面中的元素還沒有加載解析出,找不到獲取焦點的input元素,所以可以通過以下的代碼實現(xiàn)
this.$nextTick(function() { // 告訴Vue,DOM渲染完畢后,再執(zhí)行focus()方法this.$refs.inputTiltle.focus()
})
14 實現(xiàn)數(shù)據(jù)進出的動畫效果
【Item】
使用<transtion></transtion>標簽包裹