Vue CLI 本地存储 自定义事件

3.8. WebStorage(js 本地存储)

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

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

相关API

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

备注

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

localStorage

DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>localStoragetitle>
head>
<body>
    <h2>localStorageh2>
    <button onclick="saveDate()">点我保存数据button><br/>
    <button onclick="readDate()">点我读数据button><br/>
    <button onclick="deleteDate()">点我删除数据button><br/>
    <button onclick="deleteAllDate()">点我清空数据button><br/>

    <script>
        let person = {
            name:"JOJO",
            age:20
        };

        function saveDate() {
            localStorage.setItem('msg', 'localStorage');
            localStorage.setItem('person', JSON.stringify(person));
        }
        function readDate() {
            console.log(localStorage.getItem('msg'));
            const person = localStorage.getItem('person');
            console.log(JSON.parse(person))
        }
        function deleteDate() {
            localStorage.removeItem('msg');
            localStorage.removeItem('person');
        }
        function deleteAllDate() {
            localStorage.clear();
        }
    script>
body>
html>

sessionStorage

DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>sessionStoragetitle>
head>
<body>
    <h2>sessionStorageh2>
    <button onclick="saveDate()">点我保存数据button><br/>
    <button onclick="readDate()">点我读数据button><br/>
    <button onclick="deleteDate()">点我删除数据button><br/>
    <button onclick="deleteAllDate()">点我清空数据button><br/>

    <script>
        let person = {
            name:"JOJO",
            age:20
        };

        function saveDate() {
            sessionStorage.setItem('msg', 'sessionStorage');
            sessionStorage.setItem('person', JSON.stringify(person));
        }
        function readDate() {
            console.log(sessionStorage.getItem('msg'));
            const person = sessionStorage.getItem('person');
            console.log(JSON.parse(person))
        }
        function deleteDate() {
            sessionStorage.removeItem('msg');
            sessionStorage.removeItem('person');
        }
        function deleteAllDate() {
            sessionStorage.clear();
        }
    script>
body>
html>

使用本地存储优化Todo-List

<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 MyFooter from './components/MyFooter';
    import MyHeader from './components/MyHeader';
    import MyList from './components/MyList';

    export default { 
        name:'App', 
        components:{
            MyFooter,
            MyHeader,
            MyList
        },
        data() {
            return {
                // 从本地存储中获得数据,null就创建空数组[]
                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 => todo.id !== id )
            },
            // 全选or取消全选 
            checkAllTodo(done){
                this.todos.forEach((todo)=>{
                    todo.done = done;
                });
            },
            // 清除所有已经完成的todo
            clearAllTodo(){
                this.todos = this.todos.filter((todo)=>{
                    return !todo.done;
                })
            }
        },
        // 数据发生改变就放到本地存储中,注意深度侦听,以及JSON转化为字符串
        watch: {
            todos: {
                deep:true,
                handler(value) {
                    localStorage.setItem('todos', JSON.stringify(value));
                }
            }
        }
    }
script>

<style>
    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>

3.9. 组件的自定义事件

  1. 一种组件间通信的方式,适用于:子组件 ===> 父组件
  2. 使用场景:子组件想给父组件传数据,那么就要在父组件中给子组件绑定自定义事件(事件的回调在A中)
  3. 绑定自定义事件
    1. 第一种方式,在父组件中
    2. 第二种方式,在父组件中this. r e f s . d e m o . refs.demo. refs.demo.on(‘事件名’,方法)
      <Demo ref="demo"/>
      ......
      mounted(){
          this.$refs.xxx.$on('atguigu',this.test);
      }
      
    3. 若想让自定义事件只能触发一次,可以使用once修饰符,或$once方法
  4. 触发自定义事件this.$emit(‘事件名’,数据)
  5. 解绑自定义事件this.$off(‘事件名’)
  6. 组件上也可以绑定原生DOM事件,需要使用native修饰符 @click.native="show"上面绑定自定义事件,即使绑定的是原生事件也会被认为是自定义的,需要加native,加了后就将此事件给组件的根元素
  7. 注意:通过this. r e f s . x x x . refs.xxx. refs.xxx.on(‘事件名’,回调函数)绑定自定义事件时,回调函数要么配置在methods中,要么用箭头函数,否则 this 指向会出问题

src/App.vue

<template> 
    <div class="app">
        <h1>{{ msg }},学生姓名是:{{ studentName }}h1>

        
        <School :getSchoolName="getSchoolName"/>

        
        

        
        <Student ref="student" @click.native="show"/> 
    div> 
template> 

<script> 
    // 引入组件
    import Student from './components/Student.vue';
    import School from './components/School.vue';

    export default { 
        name:'App', 
        components:{ 
            Student,
            School
        },
        data() {
            return {
                msg:'你好啊!',
                studentName:''
            }
        },
        methods: {
            getSchoolName(name) {
                console.log('App收到了学校名:', name);
            },
            getStudentName(name,...params) {
                console.log('App收到了学生名:', name, params);
                this.studentName = name;
            },
            m1() { 
                console.log('demo事件被触发了!') 
            },
            show() { 
                alert(123);
            }
        },
        mounted() {
            this.$refs.student.$on('atguigu', this.getStudentName);
            // 绑定自定义事件 // this.$refs.student.$once('atguigu',this.getStudentName) // 绑定自定义事件(一次性)
        }
    }
script>

<style scoped>
    .app{
        background-color: gray;
        padding: 5px;
    } 
style>

src/components/Student.vue

<template>
    <div class="student">
        <h2>学生姓名:{{ name }}h2>
        <h2>学生性别:{{ sex }}h2>
        <h2>当前求和为:{{number}}h2>
        <button @click="add">点我number++button>
        <button @click="sendStudentlName">把学生名给Appbutton>
        <button @click="unbind">解绑atguigu事件button>
        <button @click="death">销毁当前Student组件的实例(vc)button>
    div>
template>

<script>
    export default {
        name:'Student',
        data() {
            return {
                name: "liqb",
                sex: '男',
                number:0
            }
        },
        methods: {
            add() {
                console.log('add回调被调用了');
                this.number++;
            },
            sendStudentlName() {
                // 触发Student组件实例身上的atguigu事件
                this.$emit('atguigu', this.name, 666, 888, 900);
                // this.$emit('demo');
                // this.$emit('click');
            },
            unbind() {
                // 解绑
                this.$off('atguigu') // 解绑一个自定义事件
                // this.$off(['atguigu','demo']) // 解绑多个自定义事件
                // this.$off() // 解绑所有的自定义事件
            },
            death() {
                // 销毁了当前Student组件的实例,销毁后所有Student实例的自定义事件全都不奏效
                this.$destroy();
            }
        }
    }
script>

<style lang="less" scoped>
    .student{background-color: pink;padding: 5px;margin-top: 30px;}
style>

src/components/School.vue

<template>
    <div class="school">
        <h2 >学校地址:{{address}}h2>
        <h2>学校名称:{{name}}h2>
        <button @click="sendSchoolName">把学校名给Appbutton>
    div>
template>

<script>
    export default {
        name:'School',
        props: ['getSchoolName'],
        data() {
            return {
                name: "莆田学院",
                address: '福建莆田'
            }
        },
        methods: {
            sendSchoolName() {
                this.getSchoolName(this.name);
            }
        }
    }
script>

<style scoped> 
    .school{ 
        background-color: skyblue; 
    } 
style>

使用自定义事件优化Todo-List

src/App.vue

<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 MyFooter from './components/MyFooter';
    import MyHeader from './components/MyHeader';
    import MyList from './components/MyList';

    export default { 
        name:'App', 
        components:{
            MyFooter,
            MyHeader,
            MyList
        },
        data() {
            return {
                // 从本地存储中获得数据,null就创建空数组[]
                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 => todo.id !== id )
            },
            // 全选or取消全选 
            checkAllTodo(done){
                this.todos.forEach((todo)=>{
                    todo.done = done;
                });
            },
            // 清除所有已经完成的todo
            clearAllTodo(){
                this.todos = this.todos.filter((todo)=>{
                    return !todo.done;
                })
            }
        },
        // 数据发生改变就放到本地存储中,注意深度侦听,以及JSON转化为字符串
        watch: {
            todos: {
                deep:true,
                handler(value) {
                    localStorage.setItem('todos', JSON.stringify(value));
                }
            }
        }
    }
script>

<style>
    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>

src/components/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: "Header",
        data(){
            return {
                title:'' // 收集用户输入的title
            }
        },
        methods: {
            add(){
                // 校验数据
                if(!this.title.trim()) return alert('输入不能为空')
                // 将用户的输入包装成一个todo对象
                const todoObj = { 
                    id: nanoid(), 
                    title:this.title, 
                    done:false 
                };
                // 通知App组件去添加一个todo对象
                this.$emit('addTodo', todoObj);
                // 清空输入
                this.title = '';
            }
        }
    }
script>

<style scoped>
    .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>

src/components/MyFooter

<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: "Footer",
        props: ['todos'],
        computed: {
            // 总数
            total() {
              return this.todos.length;  
            },
            // 已完成数
            doneTotal() {
                // 此处使用reduce方法做条件统计
                return this.todos.reduce((pre, todo) => pre + (todo.done ? 1 : 0), 0);
            },
            // 控制全选框
            isAll:{
                // 全选框是否勾选
                get(){
                    return this.doneTotal === this.total && this.total > 0;
                },
                // isAll被修改时set被调用
                set(value){
                    this.$emit('checkAllTodo', value);
                }
            }
        },
        methods: {
            clearAll() {
                this.$emit('clearAllTodo');
            }
        }
    }
script>

<style scoped>
    .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>

你可能感兴趣的:(vue,vue.js,javascript,前端)