目录
这里推荐一个消息订阅与发布的js库,pubsub-js(publish发布 subscribe订阅 )
二、 TodoList利用消息订阅来修改Item的删除事件!
本节素材已上传至Gitee:yihaohhh/我爱Vue - Gitee.com
一、 消息订阅与发布
消息订阅与发布这里类比成以前家庭的报纸订阅与发布:
- 订阅报纸:住址
- 邮递员送报纸:报纸
消息订阅也是如此:
- 订阅消息:手机号(微信号)
- 发布消息:消息的内容
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
总结:
-
一种组件间通信的方式,适用于<span style="color:red">任意组件间通信</span>。
-
使用步骤:
-
安装pubsub:
npm i pubsub-js
-
引入:
import pubsub from 'pubsub-js'
-
接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的<span style="color:red">回调留在A组件自身。</span>
methods(){ demo(data){......} } ...... mounted() { this.pid = pubsub.subscribe('xxx',this.demo) //订阅消息 }
-
提供数据:
pubsub.publish('xxx',数据)
-
最好在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()
})
},
-
语法:
this.$nextTick(回调函数)
-
作用:在下一次 DOM 更新结束后执行其指定的回调。
-
什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行。