IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Vue2学习day06—浏览器本地存储&组件的自定义事件&全局事件总线(GlobalEventBus)&消息订阅与发布(pubsub)&nextTick -> 正文阅读

[移动开发]Vue2学习day06—浏览器本地存储&组件的自定义事件&全局事件总线(GlobalEventBus)&消息订阅与发布(pubsub)&nextTick

Vue2学习day06—浏览器本地存储&组件的自定义事件&全局事件总线(GlobalEventBus)&消息订阅与发布(pubsub)&nextTick

浏览器本地存储

  • 存储内容大小一般支持5MB左右(不同浏览器可能还不一样)
  • 浏览器端通过 Window.sessionStorage 和 Window.localStorage 属性来实现本地存储机制。
  • 相关API:
    • xxxxxStorage.setItem(‘key’, ‘value’);
      该方法接受一个键和值作为参数,会把键值对添加到存储中,如果键名存在,则更新其对应的值。
    • xxxxxStorage.getItem(‘person’);
      该方法接受一个键名作为参数,返回键名对应的值。
    • xxxxxStorage.removeItem(‘key’);
      该方法接受一个键名作为参数,并把该键名从存储中删除。
    • xxxxxStorage.clear()
      该方法会清空存储中的所有数据。
  • 示例:
    • localStorage
      <!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <meta http-equiv="X-UA-Compatible" content="IE=edge">
          <meta name="viewport" content="width=device-width, initial-scale=1.0">
          <title>localStorage</title>
      </head>
      <body>
          <h2>localStorage</h2>
          <button onclick="saveData()">点我保存一个数据</button>
          <button onclick="readData()">点我读取一个数据</button>
          <button onclick="deleteData()">点我删除一个数据</button>
          <button onclick="deleteAllData()">点我清空数据</button>
      
          <script type="text/javascript">
              let p = {name:'张三',age:18}
              function saveData(){
                  // window.localStorage key value 都得是字符串
                  localStorage.setItem('msg','hello!')
                  // 会自动调对象的toString
                  // localStorage.setItem('person',p)
                  localStorage.setItem('person',JSON.stringify(p))
              }
      
              function readData(){
                  console.log(localStorage.getItem('msg'))
                  const person = localStorage.getItem('person')
                  console.log(JSON.parse(person))
              }
      
              function deleteData(){
                  localStorage.removeItem('msg')
              }
      
              function deleteAllData(){
                  localStorage.clear()
              }
          </script>
          
      </body>
      </html>
      
    • sessionStorage
      <!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <meta http-equiv="X-UA-Compatible" content="IE=edge">
          <meta name="viewport" content="width=device-width, initial-scale=1.0">
          <title>sessionStorage</title>
      </head>
      <body>
          <h2>sessionStorage  浏览器一关就没了</h2>
          <button onclick="saveData()">点我保存一个数据</button>
          <button onclick="readData()">点我读取一个数据</button>
          <button onclick="deleteData()">点我删除一个数据</button>
          <button onclick="deleteAllData()">点我清空数据</button>
      
          <script type="text/javascript">
              let p = {name:'张三',age:18}
              function saveData(){
                  // window.sessionStorage key value 都得是字符串
                  sessionStorage.setItem('msg','hello!')
                  // 会自动调对象的toString
                  // sessionStorage.setItem('person',p)
                  sessionStorage.setItem('person',JSON.stringify(p))
              }
      
              function readData(){
                  console.log(sessionStorage.getItem('msg'))
                  const person = sessionStorage.getItem('person')
                  console.log(JSON.parse(person))
              }
      
              function deleteData(){
                  sessionStorage.removeItem('msg')
              }
      
              function deleteAllData(){
                  sessionStorage.clear()
              }
          </script>
          
      </body>
      </html>
      

TodoList案例实现本地存储

学习了浏览器本地存储后,我们来将上篇博客的TodoList案例实现本地存储,我们使用watch对todos进行监听,这样todos改变的时候就能调用我们编写的相关逻辑

watch:{
    // 默认未开始深度监视,只会监测数组的变化,不会监测数组中对象的变化,所以我们要开启深度监视,也就不能使用简写形式
    /*todos(value){
        // 存储
        localStorage.setItem('todos',JSON.stringify(value))
    }*/
    todos:{
        deep:true,
        handler(value){
            localStorage.setItem('todos',JSON.stringify(value))
        }
    }
}

记得修改data中存放的todos数据,todos中的数据要从本地存储中读出

// 若为null 在后面添加[]进行处理  
todos:JSON.parse(localStorage.getItem('todos')) || []

组件的自定义事件

之前我们在TodoList案例中子组件向父组件传递数据,是通过父组件事先定义的函数,父组件将此函数传递给子组件,子组件接收后进行调用的方式,下面我们学习子组件向父组件传递数据的另一种方法:组件的自定义事件

  • 一种组件间通信的方式,适用于:子组件 ===> 父组件
  • 使用场景:A是父组件,B是子组件,B想给A传数据,那么就要在A中给B绑定自定义事件(事件的回调在A中)。
  • 绑定自定义事件:
    • 第一种方式,在父组件中:<Demo @atguigu="test"/><Demo v-on:atguigu="test"/>
    • 第二种方式,在父组件中:
      <Demo ref="demo"/>
      ......
      mounted(){
         this.$refs.xxx.$on('atguigu',this.test)
      }
      
    • 若想让自定义事件只能触发一次,可以使用once修饰符,或$once方法。
      • 触发自定义事件:this.$emit('atguigu',数据)
  • 解绑自定义事件this.$off('atguigu')
  • 组件上也可以绑定原生DOM事件,需要使用native修饰符。
  • 注意:通过this.$refs.xxx.$on('atguigu',回调)绑定自定义事件时,回调要么配置在methods中要么用箭头函数,否则this指向会出问题!
  • 示例
    • Student组件(数据传递使用案例TodoList中的方法)
      <template>
        <div class="school">
            <h2 >学校名称:{{name}}</h2>
            <h2>学校地址:{{address}}</h2>
            <button @click="sendSchoolName(name)">传递学校名称</button>
        </div>
      </template>
      
      <script>
          export default {
              name:'School',
              props:['getSchoolName'],
              data(){
                  return{
                      name:'曲阜师范大学',
                      address:'曲阜',
                  }
              },
              methods:{
                  sendSchoolName(schoolName){
                      this.getSchoolName(schoolName)
                  }
              }   
          }
      </script>
      
    • Student组件(数据传递使用自定义事件,同时顺带验证destroy会销毁自定义事件)
      <template>
        <div class="student">
            <h2>学生姓名:{{name}}</h2>
            <h2>学生性别:{{sex}}</h2>
            <h2>当前求和为:{{number}}</h2>
            <button @click="add">点我number++</button>
            <button @click="sendStudentName(name)">传递学生名给App</button>
            <button @click="unbind">解绑atguigu事件</button>
            <button @click="death">销毁当前Student组件的实例对象(vc)</button>
        </div>
      </template>
      
      <script>
          export default {
              name:'Student',
              data(){
                  return{
                      name:'张三',
                      sex:'男',
                      number:0
                  }
              },
              methods:{
                  sendStudentName(){
                      // 触发Student组件实例对象身上的atguigu事件
                      this.$emit('atguigu',this.name,666,888,999)
                      
                      //this.$emit('demo')
      
                  },
                  unbind(){
                      // 解绑一个自定义事件
                       this.$off('atguigu')
                      // 解绑多个自定义事件
                      // this.$off('atguigu','demo')
                      // 解绑所有的自定义事件
                      // this.$off()
                  },
                  death(){
                      this.$destroy()// 销毁了当前Student组件的实例,销毁后所有Student实例的自定义事件全都不奏效了
                  },
                  add(){
                      console.log('add回调被调用了')
                      this.number++
                  }
              }
          }
      </script>
      
    • App组件
      <template>
       <div class="app">
             <h1>{{msg}},学生姓名是:{{studentName}}</h1>
             <!-- 通过父组件给子组件传递函数类型的props,实现子给父传递数据 -->
             <School :getSchoolName = "getSchoolName" />
             <!-- 给Student组件实例对象绑定一个事件 -->
              <!-- 通过父组件给子组件绑定一个自定义事件,实现子给父传递数据(第一种写法使用v-on或其简写形式@) -->
             <Student v-on:atguigu="getStudentName" @demo="m1"/>
              <!-- 通过父组件给子组件绑定一个自定义事件,实现子给父传递数据(第一种写法使用ref) -->
             <Student ref="student" @click="show"/>
             <!-- 声明是原生dom事件 -->
             <Student ref="student" @click.native="show"/>
       </div>
      </template>
      
      <script>
         // 引入School组件
         import School from './components/School'
         import Student from './components/Student'
      
      
         export default {
             name:'App',
             components:{
                 Student,
                 School
             },
             data(){
                 return{
                     msg:'您好呀',
                     studentName:''
                 }
             },
             methods:{
                 getSchoolName(name){
                     console.log('收到了学校名:'+name)
                 },
                 // 无论传递多少个参数,接收第一个为name,其余的封装到params数组
                 getStudentName(name,...params){
                     console.log('收到了学生名:',name,params)
                     this.studentName = name
                 },
                 m1(){
                     console.log("demo事件被触发了")
                 },
                 show(){
                     alert('原生dom事件')
                 }
             },
             mounted(){
                 // 更灵活,可设置定时器
                 /*setTimeout(()=>{
                     // 绑定自定义事件
                     // this.$refs.student.$on('atguigu',this.getStudentName)
                     // 只触发一次
                     this.$refs.student.$once('atguigu',this.getStudentName)
                 },3000)*/
                 this.$refs.student.$on('atguigu',this.getStudentName)
                 // 写成箭头函数this才为当前vc,否则为触发atguigu的vc即Student组件实例对象
                 /*this.$refs.student.$on('atguigu',(name,...params)=>{
                     console.log('App收到了学生名',name,params)
                     this.studentName = name
                 })*/
      
             }
         }
      </script>
      

TodoList案例优化,子组件==>父组件使用组价自定义事件实现

学习了组件自定义事件之后,我们继续给TodoList案例做下优化

  • ‘父亲’:App组件
    <TodoHeader @addTodo="addTodo" />
    <TodoFooter :todos="todos" @checkAllTodo="checkAllTodo" @clearAllTodo="clearAllTodo" />
    
  • ‘孩子’:TodoHeader组件
    • 这里不再需要接收其父亲App组件传递的函数了,直接触发相应的自定义事件即可。删除props中对应的接收数据,将this.addTodo(todoObj)改为this.$emit('addTodo',todoObj)
  • ‘孩子’:TodoFooter组件
      • 同样不再需要接收其父亲App组件传递的函数了,直接触发相应的自定义事件即可。删除props中对应的接收数据,并将this.checkAllTodo(value)改为this.$emit('checkAllTodo',value) this.clearAllTodo()改为this.$emit('clearAllTodo')

全局事件总线(GlobalEventBus):任意组件间通信

分析:
在这里插入图片描述
x需要具备的条件:

  • 所有组件都能看到
  • x可以调用到 o n 、 on、 onoff、$emit

全局事件总线(GlobalEventBus)

  • 一种组件间通信的方式,适用于任意组件间通信
  • 安装全局事件总线:
     new Vue({
     	......
     	beforeCreate() {
     		Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm
     	},
         ......
     }) 
     ```
    
  • 使用事件总线:
    • 接收数据:A组件想接收数据,则在A组件中给$bus绑定自定义事件,事件的回调留在A组件自身。
      methods(){
        demo(data){......}
      }
      ......
      mounted() {
        this.$bus.$on('xxxx',this.demo)
      }
      
    • 提供数据:this.$bus.$emit('xxxx',数据)
  • 最好在beforeDestroy钩子中,用$off去解绑当前组件所用到的事件。
  • 示例(下面演示两个兄弟间传递数据):
    • main.js中安装全局事件总线
      new Vue({
          .....
          
          beforeCreate() {
          	// 安装去全局事件总线
              Vue.prototype.$bus = this
          },
      })
      
    • Student组件
      <template>
        <div class="student">
            <h2>学生姓名:{{name}}</h2>
            <h2>学生性别:{{sex}}</h2>
            <button @click="sendStudentName">把学生名给School组件</button>
        </div>
      </template>
      
      <script>
          export default {
              name:'Student',
              data(){
                  return{
                      name:'张三',
                      sex:'男',
                  }
              },
              mounted(){
                  // console.log('student',this.x)
              },
              methods:{
                  sendStudentName(){
                      this.$bus.$emit('hello',this.name)
                  }
              }
             
          }
      </script>
      
    • School组件
      <template>
        <div class="school">
            <h2 >学校名称:{{name}}</h2>
            <h2>学校地址:{{address}}</h2>
        </div>
      </template>
      
      <script>
          export default {
              name:'School',
              props:['getSchoolName'],
              data(){
                  return{
                      name:'曲阜师范大学',
                      address:'曲阜',
                  }
              },
              mounted(){
                  //console.log('school',this.x)
                  // 所有的事件名不能重复,所有的组件都在用$bus
                  this.$bus.$on('hello',(data)=>{
                      console.log('我是School组件,收到了数据',data)
                  })
              },
              beforeDestroy(){
                  // 用完了要解绑,不要占用资源
                  this.$bus.$off('hello')
              }
          }
      </script>
      
    • 最后在App组件中注册使用School组件和Student组件即可

TodoList案例优化,孙子组件==>爷爷组件使用全局事件总线实现

之前我们在TodoList案例中孙子TodoItem组件向其爷爷App组件中传递数据是通过其父亲TodoList做了个过渡,,当我们学习完全局事件总线后,我们来优化下该功能

  • 首先我们去掉App组件和TodoList传递函数的部分,更改后App组件中该部分变为
    <TodoList :todos="todos" />
    
  • TodoList该部分变为:
    <TodoItem v-for="todoObj in todos" :key="todoObj.id"  :todo="todoObj" />
    
  • 之后我们将TodoList和TodoItem中接收相关函数的props的相关数据项去掉,注意不要删多了,只删除相应的函数的参数即可
  • main.js中安装全局事件总线
    new Vue({
        .....
        
        beforeCreate() {
        	// 安装去全局事件总线
            Vue.prototype.$bus = this
        },
    })
    
  • 给App组件中的$bus绑定自定义事件,注意要在组件销毁前进行解绑,释放资源
    mounted(){
        this.$bus.$on('checkTodo',this.checkTodo)
        this.$bus.$on('deleteTodo',this.deleteTodo)
    },
    beforeDestroy(){
        this.$bus.$off('checkTodo')
        this.$bus.$off('deleteTodo')
    }
    
  • TodoList组件给App组件提供数据
    methods:{
      handleCheck(id){
        //console.log(id)
        // 通知App组件将对应的todo对象的done值取反
        //this.checkTodo(id)
        this.$bus.$emit('checkTodo',id)
      },
      handleDelete(id){
        if(confirm('确定删除吗')){
          //this.deleteTodo(id)
          this.$bus.$emit('deleteTodo',id)
        }
      }
    }
    

消息订阅与发布(pubsub)

消息订阅与发布

  • 一种组件间通信的方式,适用于任意组件间通信

  • 使用步骤:

    • 安装pubsub:npm i pubsub-js
    • 引入: import pubsub from 'pubsub-js'
    • 接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身。
      methods(){
         demo(data){......}
       }
       ......
       mounted() {
         this.pid = pubsub.subscribe('xxx',this.demo) //订阅消息
       }
      
    • 提供数据:pubsub.publish('xxx',数据)
    • 最好在beforeDestroy钩子中,用PubSub.unsubscribe(pid)去取消订阅。
    • 第三方库pubsub,js
  • 安装第三方消息订阅与发布库:

    npm install pubsub-js
    
  • 在School组件引入第三方库

    import pubsub from 'pubsub-js'
    
  • School组件要接收Student组件中的数据,要在School组件中订阅消息

    methods:{
        demo(msgName,data){
            console.log('有人发布了hello消息,hello消息的回调执行了',msgName,data)
        }
    },
    mounted(){
        /*this.pubId = pubsub.subscribe('hello',function(msgName,data){
            // 里面的this是undefined,因为用的是第三方库,所以我们要使用箭头函数,或者抽离函数到methods
            console.log('有人发布了hello消息,hello消息的回调执行了',msgName,data)
        })*/
        this.pubId = pubsub.subscribe('hello',this.demo)
    },
    beforeDestroy(){
        pubsub.unsubscribe(this.pubId)
    }
    
  • Student组件传递数据,同样需要也需要引入pubsub包

    pubsub.publish('hello',this.name)
    

nextTick

  1. 语法:this.$nextTick(回调函数)
  2. 作用:在下一次 DOM 更新结束后执行其指定的回调。
  3. 什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行。

给TodoList案例添加编辑功能,点击编辑按钮,列表中的数据变为输入框,并获得焦点,失去焦点触发事件,保存数据。这里点击编辑输入框会获取焦点,需要使用nextTick来实现,否则输入框还没渲染到界面,就执行获取焦点的方法,这是获取不到焦点的
修改后的TodoItem组件

<template>
  <li>
    <label>
        ......
        <span v-show="!todo.isEdit">{{todo.title}}</span>
        <input 
          v-show="todo.isEdit" 
          type="text" 
          :value="todo.title" 
          @blur="handleBlur(todo,$event)"
          ref="inputTitle"
          >
    </label>
    	......
    <button v-show="!todo.isEdit" class="btn btn-edit" @click="handleEdit(todo)">编辑</button>
  </li>
</template>

<script>
    import pubsub from 'pubsub-js'
    export default {
        	....
        methods:{
          	......
          // 编辑
          handleEdit(todo){
            if(todo.hasOwnProperty('isEdit')){
              todo.isEdit = true
            }else{
              this.$set(todo,'isEdit',true)
            }
            // nextTick所指定的回调会在下次dom节点更新之后执行
            this.$nextTick(function(){
              this.$refs.inputTitle.focus()
            })
          },
          // 失去焦点回调(真正执行修改逻辑)
          handleBlur(todo,e){
            if(!e.target.value.trim()) return alert('输入不能为空')
            this.$bus.$emit('updateTodo',todo.id,e.target.value)
            todo.isEdit = false
          }
        }
    }
</script>

在App组件中添加修改逻辑

updateTodo(id,title){
    this.todos.forEach((todo)=>{
        if(todo.id === id) todo.title = title
    })
},
  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-03-17 22:19:09  更:2022-03-17 22:20:30 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年1日历 -2025/1/31 17:54:55-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码