「这是我参与11月更文挑战的第6天,活动详情查看:2021最后一次更文挑战」。 最近 接到领导安排的一个任务,是关于微信小程序的,有一个文章列表,类型 有 图文和视频,并且 视频在列表中可以直接播放,现有功能已经写好了,现在要处理一下优化问题,就是播放中的视频只能存在一个,也就是在点击播放一个视频的时候,其他播放中的视频要暂停。
想了下 蛮简单,利用 Api 的 createVideoContext和pause() 就能解决了
按说应该开发这个功能的人分分钟不就加上了,当前页面 循环判断不就行了
事出反常必有妖 ,当我看了下代码的时候,就明白了,果然 没我想象的那么简单,组件嵌套了 多层
-
pageA(主页)中引入了 组件A(列表) 传进(props)列表数据 list
-
组件A中 又引入 组件B 根据list循环 组件B 传入 详情 detail数据
-
在组件B图文和视频 接收 detail 进行渲染
在不动原有代码的基础上优化起来就有一定难度,虽然麻烦,但也是可以处理的,通过父子孙之间的组件通信就能解决, 但随后又考虑到,这个视频优化需求,还涉及到详情页,和部分活动页面,等等等等, 要是挨个写可就没完没了了,于是,想把视频 播放这个功能 单独封成一个组件videoHandle,只需要把所有video标签替换成videoHandle就可以了,一劳永逸。
接下来就是想办法处理所有 videoHandle组件之间的通信了,最终采用了 发布订阅模式来解决
项目是基于 uniapp 的,页面文件遵循 Vue 单文件组件 (SFC) 规范
手写 发布订阅
创建 myevent.js
export default class myevent{
// 存储事件队列
constructor(){
this.msg = {}
}
/**
* subscribe 订阅事件
* @param {*} key 事件标识符
* @param {*} fn 事件方法
*/
subscribe(key, fn){
...
}
/**
* 发布队列中所有事件,通知所有订阅者
*/
publish() {
...
}
/**
* 事件删除,传入key值(fn选填)
* @param {*} key
* @param {*} fn
*/
remove(key, fn){
...
}
/**
* 获取当前 队列数
*/
getQueue(key){
...
}
}
复制代码
订阅事件
subscribe(key, fn){
// 如果回调函数不是个方法 直接返回
if(typeof fn != 'function') return
// 如果此队列不存在,创建个新的
if(!this.msg[key]) this.msg[key] = []
// 添加 事件的到消息队列中去
this.msg[key].push(fn)
}
复制代码
发布通知订阅者
为了方便传入接收多个参数 使用了 arguments
publish() {
// 获取参数key ,并删除第一个参数
let key = Array.prototype.shift.call(arguments)
let fns = this.msg(key)
// 事件为空就返回
if(!fns||!fns.length) return
fns.forEach(res => {
// 执行事件,并传入参数
res.apply(null, arguments)
})
}
复制代码
清除事件
remove(key, fn) {
let fns = this.msg[key]
// 如果不存在 返回
if(!fns || !fns.length) return
// 如果不传fn 则删除这个队列
if(!fn) {
delete this.msg[key]
}else {
// 循环 删除指定的 事件
for(let i=0;i<fns.length;i++){
let item = fns[i]
if(item === fn || item.fn === fn) {
fns.splice(i,1)
break
}
}
}
}
复制代码
获取当前 队列数
为了知道队列中事件 数量,可以调用此方法查看
getQueue(key){
if(!this.msg[key]) return undefined
return this.msg[key].length
}
复制代码
引入main.js
...
import myevent from '@/common/utils/myevent'
Vue.prototype.$event = new myevent()
...
复制代码
视频组件 videoHandle
在创建组件的时候,生成唯一guid,订阅eventSuspend消息,并绑定 接收消息事件 eventSuspend,当播放视频的时候,携带此guid 发布消息,通知所有订阅者(所有的videoHandle,包括此组件),接收到通知后,通过guid 来处理当前视频的播放暂停动作
具体代码如下
<template>
<video
class="my-video"
:id='id'
:src="url"
object-fit="cover"
@error="videoErrorCallback"
@play="playing"
controls
>
</video>
</template>
<script>
export default {
props: {
url: {
type: String,
default: '',
},
},
data() {
return {
id:'',
url: ''
}
},
created() {
this.id = this.$u.guid()
// 订阅 eventSuspend 消息 并绑定 this.eventSuspend()
this.$event.subscribe('eventSuspend', this.receiveSuspend)
},
methods: {
videoErrorCallback (e) {
console.log('videoErrorCallback')
console.log(e)
},
// 接收消息通知
receiveSuspend(id){
console.log('暂停播放suspend', this.id, id)
// 接收的 id与本视频id不符 则停止播放
if(id && this.id && id !== this.id){
this.pause()
}
},
// 播放时触发
playing(){
// 发布消息,通知所有订阅者
this.$event.publish('eventSuspend', this.id)
},
// 暂停视频播放
pause(){
wx.createVideoContext(this.id, this).pause()
}
},
// 退出页面时 取消订阅
destroyed () {
this.$event.remove('eventSuspend', this.eventSuspend)
},
};
</script>
<style scoped lang="scss">
.my-video{
width: 100%;
height: 100%;
}
</style>
复制代码