效果图
文件关系图
1.Vue.config.js
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
lintOnSave:false
})
Vue.js项目的配置文件。使用
@vue/cli-service
模块中的defineConfig
方法定义配置对象配置项:
transpileDependencies:true
:表示在构建过程中对依赖进行转译,确保项目的兼容性。默认情况下,Babel只会对Vue CLI创建的项目自身代码进行转译,而不会对依赖的第三方库进行转译;
lintOnSave:false
:表示在保存文件时,是否启用ESLint进行代码检查。设置为false后保存文件不会触发ESLint的检查;
2. main.js
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({
render: h => h(App),
}).$mount('#app')
Vue.js应用程序的入口文件。
导入Vue.js模块,并赋值给变量
Vue;
从当前目录导入App.vue文件,并赋值给变量
App;
Vue.config.productionTip=false
:禁用生产模式下的一些提示信息;
new Vue()
:创建一个Vue实例,将App
组件作为根组件进行渲染。在render
函数中使用箭头函数的简写语法将h
(createElement)参数传递进去,并返回了App
组件的渲染结果;
$mount('#app')
将Vue实例挂载到HTML中具有id="app"
的元素上,并显示在页面上;
3. App.vue
3.1.template
<template>
<div id="App">
<MyHeader :addTodo="addTodo"></MyHeader>
<MyList :deleteTodo="deleteTodo" :checkTodo="checkTodo" :todos="todos"></MyList>
<MyFooter :todos="todos" :checkAllTodo="checkAllTodo" :clearAllTodo="clearAllTodo"></MyFooter>
</div>
</template>
App.vue的template部分的描述
定义一个
id
为App
的div
块级元素;使用自定义组件
MyHeader
传递一个属性addTodo
。addTodo
属性指向父组件中的一个方法addTodo
,实现子组件向父组件传递数据的功能;使用自定义组件
MyList
传递三个属性deleteTodo
、checkTodo
、todos
,这些属性指向父组件中的三个方法和一个数据,实现子组件向父组件传递数据和方法的功能;使用自定义组件
MyFooter
传递三个属性todos
、checkAllTodo
、clearAllTodo
,这些属性指向子组件中的一个数据和两个方法,实现子组件向父组件传递数据和方法的功能;
3.2.script
<script>
import MyHeader from "@/components/MyHeader.vue";
import MyList from "@/components/MyList.vue";
import MyFooter from "@/components/MyFooter.vue";
export default {
name: 'App',
data(){
return{
todos:[
{id:'001',title:'测试数据A',done:false},
{id:'002',title:'测试数据B',done:false},
{id:"003",title:'测试数据C',done:false}
]
}
},
methods:{
//添加任务
addTodo(todoObj){
this.todos.unshift(todoObj);
console.log("addTodo("+todoObj+")");
},
//勾选或者取消一个任务
checkTodo(id){
this.todos.forEach(item =>{
if(item.id == id){
item.done = !item.done;
console.log("checkTodo("+id+"):"+item.done!=item.done);
return;
}
})
},
deleteTodo(id){
this.todos = this.todos.filter(item =>{
console.log("deleteTodo("+id+"):"+item.id !=id);
return item.id !=id;
})
},
checkAllTodo(done){
console.log("checkAllTodo("+done+")");
this.todos.forEach(item=> item.done =done);
},
clearAllTodo(){
console.log("clearAllTodo():"+ this.todos.filter(item => !item.done));
this.todos = this.todos.filter(item => !item.done);
}
},
components: {MyFooter, MyList, MyHeader}
}
</script>
App.vue的script部分的表述
使用
import
引入MyHeader
、MyList
、MyFooter
三个自定义组件;通过
export default
导入当前Vue组件的配置;
components
属性是一个对象,包含了三个键值对,对应引入的三个自定义组件:MyFooter
、MyList
、MyHeader;
name
属性用于指定当前组件的名称;
data
方法返回一个对象,对象中有一个todos
的属性,包含了三个任务对象;每个任务对象都有id
:任务的唯一标识,title
:标题,done
:完成状态;
addTodo
方法用于向任务列表中添加一个新的任务对象,通过unshift
方法将任务对象插入到todos
数组的开头;
checkTodo
方法用于勾选或取消一个任务,通过遍历todos
数组找到对应的任务,并修改done
属性的值;
deleteTodo
方法用于删除一个任务,通过使用filter
方法过滤掉要删除的任务,并重新赋值给todos
数组;
checkAllTodo
方法用于全选或取消全选任务,遍历todos
数组将所有任务的done
属性设置为传入的值;
clearAllTodo
方法用于清楚所有已完成的任务,通过使用filter
方法过滤掉已完成的任务,并重新赋值给todos
数组;
3.3.style
<style>
h2{
color: orange;
}
</style>
4. MyHeader.vue
4.1.template
<template>
<div class="header">
<input type="text" v-model="title"@keydown.enter="add" placeholder="请输入任务名称,按回车"/>
</div>
</template>
MyHeader.vue的template部分的描述
定义一个包含输入框
<input>
元素的<div>
块级元素;使用
v-model
绑定title
属性;使用
@keydown.enter="add"
监听键盘的Enter
事件,触发时调用add
方法;
4.2.script
<script>
import {nanoid} from "nanoid";
export default {
name: "MyHeader",
props:['addTodo'],
data(){
return{
title:''
}
},
methods:{
add(){
let todoObj ={id:nanoid(),title:this.title,done:false};
console.log("MyHeader:add():"+todoObj);
this.addTodo(todoObj);
this.title ='';
}
}
}
</script>
MyHeader.vue的script部分的描述
导入
nanoid
函数;通过
export default
导入当前Vue组件的配置;
定义名为
MyHeader
的组件;使用
props
属性声明一个名为addTodo
的prop菜鸟教程对prop的描述;在
data
方法中初始化一个名为title
的属性;定义一个名为
add
的方法,在用户按下Enter
键时会创建一个包含输入内容的todo
对象,然后通过addTodo
prop将该对象传递给父组件,最后清空输入框内容;
4.3.style
<style scoped>
.header input {
width: 560px;
height: 28px;
font-size: 14px;
border: 1px solid #ccc;
border-radius: 4px;
padding: 4px 7px;
}
.header input:focus {
outline: none;
border-color: rgba(82, 168, 236, 0.8);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(82, 168, 236, 0.6);
}
</style>
5. MyItem.vue
5.1.template
<template>
<li>
<label>
<input type="checkbox" @click="handleCheck(todo.id)" :checked="todo.done"/>
<span>{{todo.title}}</span>
</label>
<button class="btn" @click="handleDelete(todo.id,todo.title)">删除</button>
</li>
</template>
MyItem.vue的template部分的描述
定义一个
type
属性为checkbox
的复选框输入类型的input
元素;通过
@click
事件监听器绑定一个handleCheck(todo.id)
的点击事件处理函数,复选框被点击时触发;
:checked
用于动态绑定复选框的选中状态;绑定
todo.done
属性根据todo
对象的done
属性确定是否选中复选框;定义一个
button
元素,通过@click
事件监听器绑定一个;handleDelete(todo.id,todo.title)
点击事件处理函数,当按钮被点击时会触发,并传递todo.id
和todo.title
作为参数;
5.2.script
<script>
export default {
name: "MyItem",
props:['todo',"checkTodo",'deleteTodo'],
methods: {
handleCheck(id) {
console.log("MyItem:handleCheck("+id+")");
this.checkTodo(id);
},
handleDelete(id, title) {
if (confirm("确定删除" + title + "任务吗?")) {
console.log("MyItem:handleDelete("+id,title+")");
this.deleteTodo(id);
}
}
}
}
</script>
MyItem.vue的scirpt部分的描述
通过
export default
导入当前Vue组件的配置;
name
属性用于指定当前组件的名称;
props
属性是一个数组,数组中的字符串元素代表了父组件可以通过这些属性向当前组件传递数据或方法;
handleCheck
方法接受一个参数id
,使用this.checkTodo
调用父组件传递过来的checkTodo
方法,并将id
作为参数传递;
hendleDelete
方法接受两个参数id
和title
,通过弹窗confirm
确认是否删除,如果确认,则调用this.deleteTodo(id)
方法,this.deleteTodo(id)
调用父组件传递过来的deleteTodo
方法,并将id
作为参数传递;
5.3.style
<style scoped>
li {
list-style: none;
height: 36px;
width: 560px;
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: -1;
}
li button {
float: right;
display: none;
margin-top: 5px;
}
li:before {
cursor: inherit;
}
li:last-child {
border-bottom: none;
}
li:hover {
background-color: #eee;
}
li:hover button {
display: block;
}
</style>
6. MyList.vue
6.1.template
<template>
<ul class="main">
<MyItem v-for="t in todos" :key="t.id"
:todo="t"
:checkTodo="checkTodo"
:deleteTodo="deleteTodo"
/>
</ul>
</template>
MyList.vue的template部分的描述
定义一个
ul
元素;使用
v-for
遍历名为todos
的数组,数组中的每个元素都会生成一个MyItem
组件的实例;
:key
用来设置每个生产实例的唯一标识,这里使用每个todo
对象的id
属性;通过
:todo
属性将遍历到的todo
对象传递给子组件;
:checkTodo
和:deleteTodo
属性传递两个函数checkTodo
和deleteTodo
用于处理点击事件;
6.2.script
<script>
import MyItem from "@/components/MyItem.vue";
export default {
name: "MyList",
props: ['todos', "checkTodo",'deleteTodo'],
components: {MyItem}
}
</script>
MyList.vue的script部分的描述
使用ES6的模块导入语法导入名为
MyItem
的组件;通过
export default
导入当前Vue组件的配置;
name
属性用于指定当前组件的名称;
props
属性是一个数组,数组中的字符串元素代表了父组件通过这些属性名向当前组件传递数据或方法;
components
属性代表将MyItem
组件注册为当前组件的局部组件;
6.3.style
<style scoped>
.main {
width: 570px;
margin-left: 0px;
border: 1px solid #ddd;
border-radius: 2px;
padding: 0px;
}
</style>
7. MyFooter.vue
7.1.template
<template>
<div class="footer" v-show="true">
<label>
<input type="checkbox" v-model="isAll"/>
全选
</label>
<span>
<span>已完成{{doneTotal}}</span>/全部{{total}}
</span>
<button @click="clearAll">删除已完成任务</button>
</div>
</template>
MyFooter.vue的template部分的描述
v-show
将一个class
为footer
的div
块元素始终显示在页面上;将
type
属性为checkbox
的复选框用v-model
绑定在isAll
属性,表示是否全部选中;使用插值表达式
{{}}
动态显示doneTotal
和total
变量的值;定义
<button>
元素,通过@click
事件监听器绑定一个clearAll
点击事件处理函数,当按钮被点击时会触发;
7.2.script
<script>
export default {
name: "MyFooter",
props:['todos','checkAllTodo','clearAllTodo'],
computed:{
doneTotal(){
console.log("MyFooter:doTotal():"+this.todos.reduce((pre,todo) =>pre + (todo.done ? 1:0),0));
return this.todos.reduce((pre,todo) =>pre + (todo.done ? 1:0),0);
},
total(){
console.log("MyFooter:total():"+this.todos.length);
return this.todos.length;
},
isAll:{
get(){
console.log("MyFooter:isAll:get():"+this.total===this.doneTotal && this.total >0);
return this.total === this.doneTotal && this.total >0;
},
set(value){
console.log("MyFooter:isAll:set("+value+"):"+this.checkAllTodo(value));
this.checkAllTodo(value);
}
}
},
methods:{
clearAll(){
console.log("MyFooter:clearAll():"+this.clearAllTodo);
this.clearAllTodo();
}
}
}
</script>
MyFooter.vue的script部分的描述
通过
export default
导入当前Vue组件的配置;
name
属性用来指定当前组件的名称;
props
属性是一个数组,数组中的字符串元素代表了父组件可以通过这些属性向当前组件传递数据或方法;
doneTotal
计算已完成任务的数量,使用reduce
方法遍历todos
数组并统计done
属性为true
的元素数量;
total
计算总任务的数量,直接返回todos
数组的长度;
isAll
计算用于控制全选复选框的状态。通过get
函数和set
函数实现对全选状态的监听和修改;
clearAll
方法用于清除所有已完成任务,当触发清除按钮时会调用父组件传递过来的clearAllTodo
方法;
7.3.style
<style scoped>
.footer {
width: 570px;
height: 40px;
line-height: 40px;
padding-left: 7px;
margin-top: 5px;
}
.footer label {
display: inline-block;
margin-right: 20px;
cursor: pointer;
}
.footer label input {
//position: relative; vertical-align: middle; margin-right: 5px;
}
.footer button {
float: right;
margin-top: 5px;
}
</style>
时贰肆年壹月捌日凌晨