Vue2 —— 学习(七)

目录

[一、TodoList 案例(第一版)](#一、TodoList 案例(第一版))

(一)组件化编码流程

1.实现静态组件

2.显示动态数据

(二)增加元素

(三)多选框状态确定

(四)删除元素

(五)统计勾上的数据

(六)全部勾上/取消

(七)清除完成的任务

二、总结:

(一)组件化的编码流程:

1.先拆分组件

2.实现动态组件:

[(二)props 适用于](#(二)props 适用于)

1.父组件到子组件传递数据(一层层太麻烦)

2.子组件到父组件传递数据

[(三)v-model 不能绑定 props 传过来的值](#(三)v-model 不能绑定 props 传过来的值)

[(四)props 传过来的东西如果是对象](#(四)props 传过来的东西如果是对象)

三、代码部分

(一)App.vue

(二)MyFooter.vue

(三)Header.vue

(四)MyItem.vue

(五)MyList.vue

(七)Main.js


一、TodoList 案例(第一版)

实现一个自定义添加项的列表 新添加的项在最上面 可以删除项 并能全选

(一)组件化编码流程

1.实现静态组件

在我们要完成的成品页面中,抽取组件,使用组件实现静态页面

我们拆出来四个组件 先实现这四个静态组件 只考虑样式 不包含数据和动态的交互

拆分组件把四个新建四个组件到文件夹里面 然后 命名 在 App 中引入

先拆样式再拆结构,看 Html 结构都是属于哪一部分的 然后分别剪切 复制到对应的组件中,并在剪切初写上对应的组件标签

然后再拆 css 把对应的 css 结构截取出来 然会放到对应的组件中

2.显示动态数据

数据类型 数组对象 不能光写名字 还要有 id 什么的

数据谁用就给谁 我们给 List

兄弟组件之间传数据 不太方便 后面会学但是我们先用一种基础的办法

所以我们把数据先把数据写到 App 中

组件间通信

(二)增加元素

父亲给儿子传:直接用 props 进行传数据就行

儿子给父亲传数据:父亲得先给儿子一个自己的函数,然后传给儿子 儿子在自己里面调用就实现了儿子给父亲传数据

兄弟间传数据:目前还办不到

先在 app 中 因为是header 想往 app 中添加数据 所以要在 app 中写个函数传给 header

html 复制代码
<div class="todo-wrap">
        <MyHeader :addTodo="addTodo" />
        <MyList :todos="todos"/>
        <MyFooter />
      </div>
javascript 复制代码
methods: {
    addTodo(todoObj) {
      this.todos.unshift(todoObj);
    },

然后在 Header 中 再写一个函数 把数据传回来

html 复制代码
 <div class="todo-header">
    <input type="text" placeholder="请输入你的任务名称,按回车键确认"   @keyup.enter="add"/>
  </div>
javascript 复制代码
 methods: {
    add(e) {
      const todoObj = { id: nanoid(), title: e.target.value, done: false };
      this.addTodo(todoObj);
    },
  },

(三)多选框状态确定

app 中

html 复制代码
 <div class="todo-wrap">
        <MyHeader :addTodo="addTodo" />
        <MyList :todos="todos" :checkTodo="checkTodo"/>
        <MyFooter />
      </div>
javascript 复制代码
 checkTodo(id){
      this.todos.forEach((todo)=>{
        if(todo.id === id) todo.done = !todo.done
      })
    }

MyList 中

html 复制代码
<MyItem
      v-for="todoObj in todos"
      :key="todoObj.id"
      :todo="todoObj"
      :checkTodo="checkTodo"
    />
javascript 复制代码
 props: ["todos", "checkTodo"],

MyItem 中

html 复制代码
   <input
        type="checkbox"
        :checked="todo.done"
        @change="handleCheck(todo.id)"
      />
javascript 复制代码
 methods: {
    handleCheck(id) {
      this.checkTodo(id);
    },
  },

用 v-model 也能修改 多选框 但是不建议使用

还是 app 中写函数然后一步步引入比较好

(四)删除元素

confirm 函数是根据用户的交互来输出 布尔值是真还是假

弹个窗 确认时才会弹窗

app 文件中

javascript 复制代码
 handleDelete(id) {
      if (confirm("确定删除吗")) {
        this.deleteTodo(id);
      }
    },

item 文件中

javascript 复制代码
handleDelete(id) {
      if (confirm("确定删除吗")) {
        this.deleteTodo(id);
      }
    },

然后把 app 中的方法通过使用props 方法 传到 item 中即可

(五)统计勾上的数据

模板代码

html 复制代码
<span> <span>已完成{{doneTotal}}</span> / 全部{{todos.length}}</span>

写在 计算函数中 返回的就是最后的 return 值 里面再加一个判断 todo.done 看是否勾上 只统计勾上的项进行计数

javascript 复制代码
computed:{
    doneTotal(){
     return this.todos.reduce((pre,todo)=>pre + (todo.done ? 1 : 0),0)
    }
  }

(六)全部勾上/取消

看是否还有项如果没有项就不显示这个删除框 v-show="total"

我们能使用 v-model 的双向绑定方式来进行绑定 能显示最开始的状态,在这个isAll 函数内部写上,如果勾上就所有都全选 ,如果取消就所有都取消

html 复制代码
<template>
  <div class="todo-footer" v-show="total">
    <label>
      <input type="checkbox" v-model="isAll" />
    </label>
    <span>
      <span>已完成{{ doneTotal }}</span> / 全部{{ total }}</span
    >
    <button class="btn btn-danger">清除已完成任务</button>
  </div>
</template>

然后在 app 里写上一个函数 用来给所有的选项都勾上或者都取消

然后用 props 连接

javascript 复制代码
checkAllTodo(done) {
      this.todos.forEach((todo) => {
        todo.done = done;
      });
    },

(七)清除完成的任务

app 中的函数

javascript 复制代码
    clearAllTodo() {
      this.todos = this.todos.filter((todo) => {
        return !todo.done;
      });
    },

footer 中的函数 先用props 引入 然后在 button 中写点击事件 使用 clearAll 方法 在其中直接调用 app 中的清除已选项的函数就行了

javascript 复制代码
    clearAll() {
      this.clearAllTodo();
    },

二、总结:

(一)组件化的编码流程:

1.先拆分组件

按功能拆,命名不能和 html 中的标签冲突,实现静态界面不考虑交互

2.实现动态组件:

考虑好数据的存放位置,看数据是被一个组件用还是被一些组件用

只有一个组件用这个数据就放在它自身就行

一些组件在用,就放到他们共同的父组件上(状态提升)状态就是我们数据改变引起了页面的变化就是状态的改变

(二)props 适用于

两种情况

1.父组件到子组件传递数据(一层层太麻烦)

在父组件中 :传递的数据名

然后直接在子组件中用 props 接收即可

2.子组件到父组件传递数据

父组件要先给子组件传递一个函数(用 props)

子组件在合适的时候调用函数 通过传参的形式把数据传过去

(三)v-model 不能绑定 props 传过来的值

v-model 是双向的,如果绑定就说明我们的输入和勾选的行为都会影响 props 传进来的值,这就不符合我们所理解的规则了 (props 不可修改)

(四)props 传过来的东西如果是对象

里面属性能修改而且不报错 但是我们不推荐这么做

三、代码部分

(一)App.vue

javascript 复制代码
<template>
  <div id="root">
    <div class="todo-container">
      <div class="todo-wrap">
        <MyHeader :addTodo="addTodo" />
        <MyList
          :todos="todos"
          :checkTodo="checkTodo"
          :deleteTodo="deleteTodo"
        />
        <MyFooter
          :todos="todos"
          :checkAllTodo="checkAllTodo"
          :clearAllTodo="clearAllTodo"
        />
      </div>
    </div>
  </div>
</template>
<script>
import MyHeader from "./components/MyHeader.vue";
import MyFooter from "./components/MyFooter.vue";
import MyList from "./components/MyList.vue";
export default {
  name: "app",
  components: {
    MyHeader,
    MyFooter,
    MyList,
  },
  data() {
    return {
      todos: [
        { id: "001", title: "吃饭", done: true },
        { id: "002", title: "喝水", done: false },
        { id: "003", title: "玩游戏", done: true },
      ],
    };
  },
  methods: {
    addTodo(todoObj) {
      this.todos.unshift(todoObj);
    },
    checkTodo(id) {
      this.todos.forEach((todo) => {
        if (todo.id === id) todo.done = !todo.done;
      });
    },
    deleteTodo(id) {
      this.todos = this.todos.filter((todo) => {
        return todo.id !== id;
      });
    },
    checkAllTodo(done) {
      this.todos.forEach((todo) => {
        todo.done = done;
      });
    },
    clearAllTodo() {
      this.todos = this.todos.filter((todo) => {
        return !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.vue

javascript 复制代码
<template>
  <div class="todo-footer" v-show="total">
    <label>
      <input type="checkbox" v-model="isAll" />
    </label>
    <span>
      <span>已完成{{ doneTotal }}</span> / 全部{{ total }}</span
    >
    <button class="btn btn-danger" @click="clearAll">清除已完成任务</button>
  </div>
</template>

<script>
export default {
  name: "MyFooter",
  props: ["todos", "checkAllTodo", "clearAllTodo"],
  computed: {
    total() {
      return this.todos.length;
    },
    doneTotal() {
      return this.todos.reduce((pre, todo) => pre + (todo.done ? 1 : 0), 0);
    },
    isAll: {
      get() {
        return this.doneTotal === this.total && this.total > 0;
      },
      set(value) {
        this.checkAllTodo(value);
      },
    },
  },
  methods: {
    checkAll(e) {
      console.log(e.target.checked);
      this.checkAllTodo(e.target.checked);
    },
    clearAll() {
      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>

(三)Header.vue

javascript 复制代码
<template>
  <div class="todo-header">
    <input type="text" placeholder="请输入你的任务名称,按回车键确认"   @keyup.enter="add"/>
  </div>
</template>

<script>
import { nanoid } from "nanoid";
export default {
  name: "MyHeader",
  props: ["addTodo"],
  methods: {
    add(e) {
      const todoObj = { id: nanoid(), title: e.target.value, done: false };
      this.addTodo(todoObj);
    },
  },
};
</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>

(四)MyItem.vue

javascript 复制代码
<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: "MyItem",
  props: ["todo", "checkTodo", "deleteTodo"],
  mounted() {
    console.log(this.todo);
  },
  methods: {
    handleCheck(id) {
      this.checkTodo(id);
    },
    handleDelete(id) {
      if (confirm("确定删除吗")) {
        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: #ddd;
}
li:hover button {
  display: block;
}
</style>

(五)MyList.vue

javascript 复制代码
<template>
  <ul class="todo-main">
    <MyItem
      v-for="todoObj in todos"
      :key="todoObj.id"
      :todo="todoObj"
      :checkTodo="checkTodo"
      :deleteTodo="deleteTodo"
    />
  </ul>
</template>

<script>
import MyItem from "./MyItem.vue";
export default {
  name: "MyList",
  components: {
    MyItem,
  },
  props: ["todos", "checkTodo","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>

(七)Main.js

javascript 复制代码
import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

new Vue({
  el: '#app',
  render: h => h(App),

})
相关推荐
木子七2 分钟前
vue3-setup中使用响应式
前端·vue
廖子默12 分钟前
提供html2canvas+jsPDF将HTML页面以A4纸方式导出为PDF后,内容分页时存在截断的解决思路
前端·pdf·html
Moment14 分钟前
毕业半年,终于拥有了两个近 500 star 的开源项目了 🤭🤭🤭
前端·后端·开源
ps酷教程21 分钟前
webrtc视频会议学习(三)
学习·webrtc
楚疏笃37 分钟前
鸿蒙学习自由流转与分布式运行环境-跨端迁移(2)
分布式·学习·harmonyos
光影少年44 分钟前
react和vue图片懒加载及实现原理
前端·vue.js·react.js
AndyGoWei1 小时前
react react-router-dom history 实现原理,看这篇就够了
前端·javascript·react.js
胡图蛋.1 小时前
vue的理解
vue.js
小仓桑1 小时前
深入理解 JavaScript 中的 AbortController
前端·javascript