【Vue】消息订阅与发布_pubsub & TodoList 编辑事件

目录

一、 消息订阅与发布

这里推荐一个消息订阅与发布的js库,pubsub-js(publish发布 subscribe订阅 )

二、 TodoList利用消息订阅来修改Item的删除事件!

三、编辑

总结:

总结不易~ 本章节对我有很大的收获, 希望对你也是!!!


本节素材已上传至Gitee:yihaohhh/我爱Vue - Gitee.com

一、 消息订阅与发布

消息订阅与发布这里类比成以前家庭的报纸订阅与发布:

  1. 订阅报纸:住址
  2. 邮递员送报纸:报纸

消息订阅也是如此:

  1. 订阅消息:手机号(微信号)
  2. 发布消息:消息的内容

A是订阅消息的人, C是发送消息的人,A订阅了消息demo然后C通过test事件携带数据666进行发送给demo

这里推荐一个消息订阅与发布的js库,pubsub-js(publish发布 subscribe订阅 )

在script内进行引入库:

import pubsub from 'pubsub-js'

School组件内进行消息订阅~

  mounted() {
    // 其中msgName就是自定义事件名, data就是传输数据
    this.pubId = pubsub.subscribe('hello', (msgName, data) => {
      // 因为这是外部库, 所以要获取当前Vue的this还是要写成箭头函数
      console.log(this)
      console.log('有人发布了hello消息, hello消息的回调执行了',msgName, data)
    })
    console.log(this.pubId)
  },

  beforeUnmount() {
    // 取消订阅 跟定时器差不多
    pubsub.unsubscribe(this.pubId)
  }

在Student组件内进行消息发布~

    <button @click="sendStudenName">把学生名给School组件</button>
    
    methods: {
        sendStudenName() {
          pubsub.publish('hello', 666) // 发布消息
        }
      }

可以看到确实很方便,但是在Vue组件中用到的并不多,因为全部都是同来全局事件通信,可是全局事件总线全部都是Vue原生的操作,所以还是全局事件总线用到的比较多~

 本节素材已上传至Gitee:yihaohhh/我爱Vue - Gitee.com

二、 TodoList利用消息订阅来修改Item的删除事件!

  • 用消息订阅来实现 App组件需要id订阅消息 Item组件来提供id 就是发布消息  
    handleDelete(id) {
      if(confirm('确定删除吗')) {
        // 用消息订阅来实现 App组件需要id订阅消息 Item组件来提供id 就是发布消息 
        pubsub.publish('deleteTodo', id)
      }
    }
  • App组件来进行消息的订阅 和 取消订阅
  mounted() {
    // 进行事件绑定
    this.$bus.$on('checkTodo', this.checkTodo)
    // this.$bus.$on('deleteTodo', this.deleteTodo)

    // 组件挂载完毕就进行订阅 进行删除
    this.pubId = pubsub.subscribe('deleteTodo', this.deleteTodo)
  },
  // 在组件即将被销毁的时候 进行解绑
  beforeUnmount() {
    // this.$bus.$off(['checkTodo', 'deleteTodo'])
    this.$bus.$off(['checkTodo'])
    // 取消订阅 跟定时器差不多
    pubsub.unsubscribe(this.pubId)
  }

注意deleteTodo事件要传入两个参数,第一个参数用_来进行占位!

    deleteTodo(_, id) {
      // filter 过滤掉我不想要的
      this.todos = this.todos.filter(todo => todo.id !== id)
    },

Vue开发者工具是不支持观察消息订阅和发送的改变的!

 本节素材已上传至Gitee:yihaohhh/我爱Vue - Gitee.com

三、编辑

我们想通过实现点击编辑后Item的每一个任务都会变成一个文本框,可以进行编辑,然后编辑完后再变回todo.title修改后的文字进行显示在页面上!

Item中添加编辑按钮 

  <button class="btn btn-danger" @click="handleDelete(todo.id)">删除</button>
  <button class="btn btn-edit" @click="handleEdit(todo)">编辑</button>

文本框的切换

    <span v-show="!todo.isEdit">{{todo.title}}</span>
    <input v-show="todo.isEdit" type="text" :value="todo.title">

在实现方法methods中添加编辑方法:handleEdit

这里注意不能生硬的对todo的属性直接进行添加, 而是要通过Vue的set进行添加属性才会是响应式的属性 

    // 编辑
    handleEdit(todo) {
      // 这是很生硬的往里加了一个数据 不会引起get 和 set 不会让浏览器重新解析todo的数据
      // todo.isEdit = true

      // 所以 要通过set来进行添加就是一个响应式的数据
      this.$set(todo, 'isEdit', true)
      console.log(todo)
    }

在编辑时,我们需要注意不需要每次都对当前的Item进行set添加isEdit属性,而是需要判断一下当前Item是否存在当前属性,不存在就添加,存在就直接修改即可,因为通过set添加后时响应式的!

    handleEdit(todo) {
      // 这是很生硬的往里加了一个数据 不会引起get 和 set 不会让浏览器重新解析todo的数据
      // todo.isEdit = true

      // 所以 要通过set来进行添加就是一个响应式的数据
      // eslint-disable-next-line no-prototype-builtins
      if(!todo.hasOwnProperty('isEdit')) {
        this.$set(todo, 'isEdit', true)
        console.log('我没有isEdit属性')
      }
      else todo.isEdit = true
      console.log(todo)
    },

那么当我们失去文本框的焦点的时候, 就要真正意义上的修改当前文字内容,所以可以通过事件对象的属性访问也就是e.target.value来进行修改当前文字!

利用全局事件总线,文本框失去焦点!

    <input type="text" v-show="todo.isEdit" :value="todo.title" @blur="handleBlur(todo, $event)">
    // 失去焦点回调(真正执行修改逻辑)
    handleBlur(todo, e) {
      todo.isEdit = false
      if(!e.target.value.trim()) return alert('输入不能为空!')
      this.$bus.$emit('updateTodo', todo.id, e.target.value)
    }

依旧在App组件内进行事件挂载:

  mounted() {
    // 进行事件绑定
    this.$bus.$on('checkTodo', this.checkTodo)
    // this.$bus.$on('deleteTodo', this.deleteTodo)

    // 组件挂载完毕就进行订阅 进行删除
    this.pubId = pubsub.subscribe('deleteTodo', this.deleteTodo)

    // 进行绑定编辑数据事件
    this.$bus.$on('updateTodo', this.updateTodo)
  },
  // 在组件即将被销毁的时候 进行解绑
  beforeUnmount() {
    // this.$bus.$off(['checkTodo', 'deleteTodo'])
    this.$bus.$off(['checkTodo', 'updateTodo'])
    // 取消订阅 跟定时器差不多
    pubsub.unsubscribe(this.pubId)
  }

创建该事件的执行方法!

    // 编辑Item文字
    updateTodo(id, title) {
      this.todos.forEach((todo) => {
        if(todo.id === id) todo.title = title
      })
    },

点击编辑后,隐藏编辑按钮:依旧是跟上面当前的isEdit判断一样,进行取反,正在编辑时就不暂时这个编辑按钮

  <button v-show="!todo.isEdit" class="btn btn-edit" @click="handleEdit(todo)">编辑</button>

 本节素材已上传至Gitee:yihaohhh/我爱Vue - Gitee.com

总结:

  1. 一种组件间通信的方式,适用于<span style="color:red">任意组件间通信</span>。

  2. 使用步骤:

    1. 安装pubsub:npm i pubsub-js

    2. 引入: import pubsub from 'pubsub-js'

    3. 接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的<span style="color:red">回调留在A组件自身。</span>

        methods(){
          demo(data){......}
        }
        ......
        mounted() {
          this.pid = pubsub.subscribe('xxx',this.demo) //订阅消息
        }
    4. 提供数据:pubsub.publish('xxx',数据)

    5. 最好在beforeDestroy钩子中,用PubSub.unsubscribe(pid)去<span style="color:red">取消订阅。</span>

但是此时编辑还是有一个巨大的bug!当我不进行文本框点击的时候,也就不能获得焦点和失去焦点,然后就不让文本框回到原来的文字状态

也就是需要点击编辑按钮的时候, 立马获取焦点即可!

方法一:为了解决顺序问题 因为此时input框还没有被渲染到页面上 所以要加定时器来延迟获得焦点

    // 编辑
    handleEdit(todo) {
      // 这是很生硬的往里加了一个数据 不会引起get 和 set 不会让浏览器重新解析todo的数据
      // todo.isEdit = true

      // 所以 要通过set来进行添加就是一个响应式的数据
      // eslint-disable-next-line no-prototype-builtins
      if(!todo.hasOwnProperty('isEdit')) {
        this.$set(todo, 'isEdit', true)
        console.log('我没有isEdit属性')
      }
      else todo.isEdit = true

      // 为了解决顺序问题 因为此时input框还没有被渲染到页面上 所以要加定时器来延迟获得焦点
      setTimeout(() => {
        this.$refs.inputTitle.focus()
      }, 200)
    },

方法二:$nextTick 当我全部运行完了 然后重新解析模板后 在运行下面这行代码

    // 编辑
    handleEdit(todo) {
      // 这是很生硬的往里加了一个数据 不会引起get 和 set 不会让浏览器重新解析todo的数据
      // todo.isEdit = true

      // 所以 要通过set来进行添加就是一个响应式的数据
      // eslint-disable-next-line no-prototype-builtins
      if(!todo.hasOwnProperty('isEdit')) {
        this.$set(todo, 'isEdit', true)
        console.log('我没有isEdit属性')
      }
      else todo.isEdit = true

      // 这句话的意思就是 当我全部运行完了 然后重新解析模板后 在运行下面这行代码
      this.$nextTick(function() {
        this.$refs.inputTitle.focus()
      })
    },

  1. 语法:this.$nextTick(回调函数)

  2. 作用:在下一次 DOM 更新结束后执行其指定的回调。

  3. 什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行。

总结不易~ 本章节对我有很大的收获, 希望对你也是!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值