• Vue.js的todolist案例(之二)用自定义事件替代props从子组件向父组件传递数据&本地存储


    饮水思源:https://www.bilibili.com/video/BV1Zy4y1K7SH?p=75

    ①底部统计

    MyFooter.vue:

    <template>
      <div>
        <label>
          <input type="checkbox" />
        </label>
        <span>
          <span>已完成{{doneTotal}}</span> / 全部{{todos.length}}
        </span>
        <button>清除已完成项目</button>
      </div>
    </template>
    
    <script>
    export default {
      name: 'MyFooter',
      props: ['todos'],
      computed: {
        doneTotal() {
          return this.todos.reduce((pre, current) => {
            if (current.done) return pre + 1;
            return pre;
          }, 0)
        }
      }
    }
    </script>
    
    <!-- Add "scoped" attribute to limit CSS to this component only -->
    <style scoped>
    
    </style>

    ②底部交互

    MyFooter.vue:

    <template>
      <div v-show="total">
        <label>
          <input type="checkbox" :checked="allDone" @click="CheckAllTodo"/>
        </label>
        <span>
          <span>已完成{{doneTotal}}</span> / 全部{{total}}
        </span>
        <button>清除已完成项目</button>
      </div>
    </template>
    
    <script>
    export default {
      name: 'MyFooter',
      props: ['todos', 'appCheckAllTodo'],
      computed: {
        total() {
          return this.todos.length
        },
        doneTotal() {
          return this.todos.reduce((pre, current) => {
            if (current.done) return pre + 1;
            return pre;
          }, 0)
        },
        allDone() {
          return this.total > 0 && this.doneTotal === this.total
        },
      },
      methods: {
        CheckAllTodo(e) {
          this.appCheckAllTodo(e.target.checked)
        }
      },
    }
    </script>
    
    <!-- Add "scoped" attribute to limit CSS to this component only -->
    <style scoped>
    
    </style>

    v-model配合计算属性set实现:

    <template>
      <div v-show="total">
        <label>
          <input type="checkbox" v-model="allDone" />
        </label>
        <span>
          <span>已完成{{doneTotal}}</span> / 全部{{total}}
        </span>
        <button>清除已完成项目</button>
      </div>
    </template>
    
    <script>
    export default {
      name: 'MyFooter',
      props: ['todos', 'appCheckAllTodo'],
      computed: {
        total() {
          return this.todos.length
        },
        doneTotal() {
          return this.todos.reduce((pre, current) => {
            if (current.done) return pre + 1;
            return pre;
          }, 0)
        },
        allDone: {
          get() {
            return this.total > 0 && this.doneTotal === this.total
          },
          set(val) {
            this.appCheckAllTodo(val)
          }
        },
      },
    }
    </script>
    
    <!-- Add "scoped" attribute to limit CSS to this component only -->
    <style scoped>
    
    </style>
    MyFooter.vue

    ③总结TodoList案例

    饮水思源:https://www.bilibili.com/video/BV1Zy4y1K7SH?p=77&spm_id_from=pageDriver

    ④本地存储

    “虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的侦听器。这就是为什么 Vue 通过 watch 选项提供了一个更通用的方法,来响应数据的变化。当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。

    深度检测:为了发现对象内部值的变化,可以在选项参数中指定 deep: true。

    查看本地存储:

    思路就是,检测todos,每当todos改变时,将todos保存到localStorage,每当启动app,就从localStorage读出数据进行初始化,几个注意点:

    1. 要把list转化为字符串存储,取出时进行字符串解析
    2. 在list中没数据时,或者第一次启动app时,注意localStorage.getItem返回null的问题
    3. 处理无法保存勾选状态的现象——启用深度检测

    App.vue:

    <template>
      <div>  
        <MyHeader :appAddItem="appAddItem" />
        <MyList :todos="todos" :appRemoveTodo="appRemoveTodo" />
        <MyFooter :todos="todos" :appCheckAllTodo="appCheckAllTodo" />
      </div>
    </template>
    
    <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: JSON.parse(localStorage.getItem('todos')) || [] // 初始化时调用一次
        }
      },
      watch: {
        todos: {
          deep: true,
          handler(newVal) {
            localStorage.setItem('todos', JSON.stringify(newVal))     
          },  
        },
      },
      methods: {
        appAddItem(x) {
          this.todos.unshift(x);
        },
        appRemoveTodo(todoId) {
          this.todos = this.todos.filter(todo => todo.id !== todoId);
        },
        appCheckAllTodo(done) {
          this.todos.forEach(todo => {
            todo.done = done
          })
        },
      },
      components: {
        MyHeader,
        MyList,
        MyFooter,
      }
    }
    </script>
    
    <style>
    
    </style>

    ④用自定义事件替代props从子组件向父组件传递数据

    MyHeader向App传数据。

    App向MyHeader绑定事件并传入回调函数(除了写在标签里外,还有更灵活的方式,详见视频):

    <template>
      <div>  
        <MyHeader @appAddItem="appAddItem" />
        <MyList :todos="todos" :appRemoveTodo="appRemoveTodo" />
        <MyFooter :todos="todos" :appCheckAllTodo="appCheckAllTodo" />
      </div>
    </template>
    
    <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: JSON.parse(localStorage.getItem('todos')) || [] // 初始化时调用一次
        }
      },
      watch: {
        todos: {
          deep: true,
          handler(newVal) {
            localStorage.setItem('todos', JSON.stringify(newVal))     
          },  
        },
      },
      methods: {
        appAddItem(x) {
          this.todos.unshift(x);
        },
        appRemoveTodo(todoId) {
          this.todos = this.todos.filter(todo => todo.id !== todoId);
        },
        appCheckAllTodo(done) {
          this.todos.forEach(todo => {
            todo.done = done
          })
        },
      },
      components: {
        MyHeader,
        MyList,
        MyFooter,
      }
    }
    </script>
    
    <style>
    
    </style>

    MyHeader中触发事件:

    <template>
      <input type="text" placeholder="输入任务,按回车确认" @keyup.enter="addItem"/>
    </template>
    
    <script>
    import {nanoid} from 'nanoid'
    
    export default {
      name: 'MyHeader',
      methods: {
        addItem(e) {
          const todoObj = {
            id: nanoid(),
            title: e.target.value,
            done: false,
          }
          // 触发绑定在自己身上的appAddItem事件,会导致调用父组件传入的回调函数
          this.$emit('appAddItem', todoObj)
        },
      },
    }
    </script>
    
    <!-- Add "scoped" attribute to limit CSS to this component only -->
    <style scoped>
    
    </style>

    通过开发者工具查看已触发的自定义事件:

  • 相关阅读:
    day39-Spring 06-Spring的AOP:带有切点的切面
    第五讲:单例模式
    day39-Spring 05-Spring的AOP:不带有切点的切面
    day39-Spring 04-CGLIB的动态代理
    day39-Spring 03-JDK的动态代理
    day39-Spring 02-AOP的概述
    第三十二讲:UML类图(下)
    ASP.NET资源大全
    ASP.NET资源大全
    ASP.NET资源大全
  • 原文地址:https://www.cnblogs.com/xkxf/p/15867131.html
Copyright © 2020-2023  润新知