学习day53

该文展示了如何使用Vue.js进行组件化编码,创建一个TodoList应用,包括拆分静态和动态组件、处理组件间交互以及实现添加、检查、删除任务功能。同时,文章介绍了如何结合本地存储来持久化应用状态,确保数据在页面刷新后仍能保留。
摘要由CSDN通过智能技术生成

今天主要是做一个案例

TodoList

组件化编码流程:

    1. 拆分静态组件:组件要按照功能点拆分,命名不要与html元素冲突


    2.实现动态组件:考虑好数据的存放位置,数据是一个组件在用,还是一些组件在用:
            1.一个组件在用:放在组件自身即可
            2.一些组件在用:放在他们共同的父组件上(状态提升)

    3.实现交互:从绑定事件开始


props适用于:

  1.父组件 ==> 子组件 通信
  2.子组件 ==> 父组件 通信(要求父组件先给子组件一个函数)


使用v-model时要切记:v-model绑定的值不能是props传过来的值,因为props是不可以修改的

props传过来的若是对象类型的值,修改对象中的属性时Vue不会报错,但不推荐这样做
 

主要是组件之间的交互

APP.vue

<template>
<div id="root">
  <div class="todo-container">
    <div class="todo-wrap">
      <MyHeaderVue :addTodo="addTodo"></MyHeaderVue>
      <MyListVue :todos="todos" :checkTodo="checkTodo" :deleteTodo="deleteTodo"></MyListVue>
      <MyFooterVue :todos="todos" :checkAllTodo="checkAllTodo"></MyFooterVue>
    </div>
  </div>
</div>
  
</template>

<script>
import MyFooterVue from './components/MyFooter.vue'
import MyHeaderVue from './components/MyHeader.vue'
import MyListVue from './components/MyList.vue'


export default {
    name:'App',
    components:{
        MyHeaderVue,
        MyFooterVue,
        MyListVue
    },
    data(){
        return{
            todos:[
                {id:'001',title:'吃饭',done:true},
                {id:'002',title:'喝酒',done:false},
                {id:'003',title:'开车',done:true}
            ]
        }
    },
    methods:{
      //添加一个todo
      addTodo(todoObj){
        this.todos.unshift(todoObj)
      },
      //勾选or勾选取消一个todo
      checkTodo(id){
        this.todos.forEach((todo)=>{
          if(todo.id===id) todo.done=!todo.done
        })
      },
      //删除一个todo
      deleteTodo(id){
        this.todos=this.todos.filter((todo)=>{
          return 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;
}
/*app*/
.todo-container {
  width: 600px;
  margin: 0 auto;
}
.todo-container .todo-wrap {
  padding: 10px;
  border: 1px solid #ddd;
  border-radius: 5px;
}

</style>

MyHeader.vue

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

<script>
import {nanoid} from 'nanoid'

export default {
    name:'MyHeader',
    props:['addTodo'],
    data(){
      return{
        title:''
      }
    },
    methods:{
      add(){
        //校验数据
        if(!this.title) return alert('输入不能为空')
        //将用户的输入包装成一个todo对象
        const todoObj={id:nanoid(),title:this.title,done:false}
        //停止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>

MyList.vue

<template>
<ul class="todo-main">
        <MyItemVue v-for="todoObj in todos" :key="todoObj.id" :todo="todoObj" :checkTodo="checkTodo" :deleteTodo="deleteTodo"></MyItemVue>
        
      </ul>
  
</template>

<script>
import MyItemVue from './MyItem.vue'

export default {
    name:'MyList',
    components:{
        MyItemVue
    },
    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>

MyItem.vue

<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',
    //声明接收todo对象
    props:['todo','checkTodo','deleteTodo'],
    methods:{
      //勾选or取消勾选
      handleCheck(id){
        //通知App组件将对应的todo.done取反
        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>

MyFooter.vue

<template>
    <div class="todo-footer" v-show="total">
        <label>
          <input type="checkbox" :checked="isAll" @change="checkAll" />
        </label>
        <span>
          <span>已完成{{doneTotal}}</span> / 全部{{total}}
        </span>
        <button class="btn btn-danger">清除已完成任务</button>
      </div>
</template>

<script>
export default {
    name:'MyFooter',
    props:['todos','checkAllTodo'],
    computed:{
      total(){
        return this.todos.length
      },
      doneTotal(){
        return this.todos.reduce((pre,todo)=> pre +(todo.done? 1:0),0)
        /**
         const x=this.todos.reduce((pre,current)=>{
          return pre +(current.done ?1:0)
        },0)
         */
      },
      isAll(){
        return this.doneTotal === this.total && this.total>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>

本地存储

1.存储内容大小一般支持5MB左右(不同浏览器可能还不一样)

2.浏览器端通过Window.sessionStorage和Window.localStorage属性来实现本地存储机制

3.相关API:

        1.xxxStorage.setItem('key', 'value'):该方法接受一个键和值作为参数,会把键值对添加到存储中,如果键名存在,则更新其对应的值
         2.xxxStorage.getItem('key'):该方法接受一个键名作为参数,返回键名对应的值
         3.xxxStorage.removeItem('key'):该方法接受一个键名作为参数,并把该键名从存储中删除
         4.xxxStorage.clear():该方法会清空存储中的所有数据
4.备注:

        1.SessionStorage存储的内容会随着浏览器窗口关闭而消失
        2.LocalStorage存储的内容,需要手动清除才会消失
        3.xxxStorage.getItem(xxx)如果 xxx 对应的 value 获取不到,那么getItem()的返回值是null
        4.JSON.parse(null)的结果依然是null
 

localStorage.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>loaclStorage</title>
</head>
<body>
    <h2>localStorage</h2>
    <button onclick="saveDate()">点我保存一个数据</button>
    <button onclick="readDate()">点我读取一个数据</button>
    <button onclick="deleteDate()">点我删除一个数据</button>
    <button onclick="deleteAllDate()">点我清空一个数据</button>
    
    
    <script type="text/javascript">
        let p={name:'张三',age:18}

        function saveDate(){
            localStorage.setItem('msg','hello')
            localStorage.setItem('person',JSON.stringify(p))
        }
        function readDate(){
            console.log(localStorage.getItem('msg'))

            const result =localStorage.getItem('person')
            console.log(JSON.parse(result))

        }
        function deleteDate(){
            localStorage.removeItem('msg')
        }
        function deleteAllDate(){
            localStorage.clear()
        }
        
    </script>
</body>
</html>

sessionStorage.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>sessionStorage</title>
</head>
<body>
    <h2>sessionStorage</h2>
    <button onclick="saveDate()">点我保存一个数据</button>
    <button onclick="readDate()">点我读取一个数据</button>
    <button onclick="deleteDate()">点我删除一个数据</button>
    <button onclick="deleteAllDate()">点我清空一个数据</button>
    
    
    <script type="text/javascript">
        let p={name:'张三',age:18}

        function saveDate(){
            sessionStorage.setItem('msg','hello')
            sessionStorage.setItem('person',JSON.stringify(p))
        }
        function readDate(){
            console.log(sessionStorage.getItem('msg'))

            const result =sessionStorage.getItem('person')
            console.log(JSON.parse(result))

        }
        function deleteDate(){
            sessionStorage.removeItem('msg')
        }
        function deleteAllDate(){
            sessionStorage.clear()
        }
        
    </script>
</body>
</html>

TodoList 本地存储

在上面案例中进行一个优化,使其添加一个本地存储的功能

<template>
<div id="root">
  <div class="todo-container">
    <div class="todo-wrap">
      <MyHeaderVue :addTodo="addTodo"></MyHeaderVue>
      <MyListVue :todos="todos" :checkTodo="checkTodo" :deleteTodo="deleteTodo"></MyListVue>
      <MyFooterVue :todos="todos" :checkAllTodo="checkAllTodo"></MyFooterVue>
    </div>
  </div>
</div>
  
</template>

<script>
import MyFooterVue from './components/MyFooter.vue'
import MyHeaderVue from './components/MyHeader.vue'
import MyListVue from './components/MyList.vue'


export default {
    name:'App',
    components:{
        MyHeaderVue,
        MyFooterVue,
        MyListVue
    },
    data(){
        return{
            todos:JSON.parse(localStorage.getItem('todos')) || []
        }
    },
    methods:{
      //添加一个todo
      addTodo(todoObj){
        this.todos.unshift(todoObj)
      },
      //勾选or勾选取消一个todo
      checkTodo(id){
        this.todos.forEach((todo)=>{
          if(todo.id===id) todo.done=!todo.done
        })
      },
      //删除一个todo
      deleteTodo(id){
        this.todos=this.todos.filter((todo)=>{
          return todo.id !==id
        })
      },
      //全选or全不选
      checkAllTodo(done){
        this.todos.forEach((todo)=>{
          todo.done=done
        })
      }
    },
    watch:{
      todos:{
        deep:true,
        handler(value){
           localStorage.setItem('todos',JSON.stringify(value))
        }
      }
    },

}
</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;
}
/*app*/
.todo-container {
  width: 600px;
  margin: 0 auto;
}
.todo-container .todo-wrap {
  padding: 10px;
  border: 1px solid #ddd;
  border-radius: 5px;
}

</style>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值