在公司某个可视化大屏项目中,大屏页面会有多个 videojs 组件,每个组件都会对应一个视频流地址。每当视频开始播放,视频流m3u8 会不断请求,即便是暂停了播放,这个请求也不会终止。大量的请求会导致页面卡顿,长此以往会带来性能问题,导致浏览器卡死甚至崩溃。
而大屏操作中,经常会用到组件联动,点击百度地图的点位,出现一个视频弹窗,点击关闭视频,其实是隐藏了视频,而视频的请求还在继续。为了解决这个问题,我花了一些时间研究,找到了解决办法。
从videojs官方文档可以查到,有一个 dispose 方法。这个方法是用来销毁 videojs 对象的。但这个方法不能直接使用,直接使用会导致一个新的问题,那就是销毁实例后 ,原本的 video 标签dom 元素也一并销毁了,这个特性从官方文档中可以看出。
其实我个人觉得,这个方法的操作 2 的特性非常不好,这样导致关闭后组件直接被销毁,导致下次触发视频弹窗(业务需求是点击百度地图图例,出现弹窗播放视频直播流),没有视频组件可以显示播放。
于是我针对这个项目组件,写了一个 Vue 的watch,用来监听监听 display 属性。在这个项目中,这个属性每个组件都有,display 为 false 是显示,true 是隐藏(别问为什么是反的,我也不知道)。代码示例如下:
<template>
<div :style="styleObject" ref="myvideojs">
<!-- <video
:id="videojsId"
class="video-js vjs-big-play-centered vjs-fluid"
style="width: 100%; height: 100%; object-fit: fill"
></video> -->
<!-- padding-top: 0,解决减小高度到一定数值后,高度不能自适应的问题 -->
<video :id="videojsId" class="video-js vjs-big-play-centered vjs-fluid" style="width: 100%; height: 100%; object-fit: fill; padding-top: 0" ref="videoPlayer"></video>
</div>
</template>
watch: {
...
display: {
handler(newVal) {
// true 代表隐藏
if (newVal) {
console.log("隐藏 :>> ")
if (this.myVideo) {
this.$nextTick(() => {
// 必须先暂停,后销毁,销毁后 dom 元素也会被移除,所以需要手动添加相同 id 的 dom
this.myVideo.dispose()
// 下面这个 dom 跟 video 标签属性一致
const videoElement = document.createElement("video")
videoElement.setAttribute("id", this.videojsId) //注意 id 要一致
videoElement.setAttribute("class", "video-js vjs-big-play-centered vjs-fluid")
videoElement.setAttribute("style", "width: 100%; height: 100%; object-fit: fill; padding-top: 0")
videoElement.setAttribute("ref", "videoPlayer")
this.$refs.myvideojs.appendChild(videoElement) //添加相同 DOM
this.myVideo = null
})
}
} else {
console.log("显示 :>> ")
}
},
},
}
下面是绘制组件的方法:
methods: {
// 绘制方法
drawChar(result) {
let that = this
if (result.length > 1) {
console.log(this.MapPoiChange)
console.log(this.option.Playtype)
let key = this.MapPoiChange ? this.MapPoiChange : this.option.Playtype ? this.option.Playtype : "SXT"
result = result.filter(v => {
return v.key == key
})[0]
} else {
result = result[0]
}
// 这些options属性也可直接设置在video标签上,见 muted
// 实例化过,修改最新的url
if (this.myVideo) {
this.myVideo.src({ type: result.type, src: result.value })
} else {
let options = {
autoplay: this.option.autoplay, // 设置自动播放
controls: true, // 显示播放的控件
width: this.component.width,
height: this.component.height,
sources: [
{
src: result.value,
type: result.type, // 告诉videojs,这是一个hls流
},
],
} // videojs的第一个参数表示的是,文档中video的id
this.myVideo = Videojs(this.videojsId, options, function onPlayerReady() {
this.on("error", function () {
// 报错信息
var mediaError = this.error()
console.log("mediaError", mediaError)
// 异常处理
that.updateData()
})
})
}
},
},
以上代码的核心有3点:
只要注意到这三个核心点,类似的问题也能迎刃而解。
1. 销毁要包裹在\$nextTick里面,不然会出现报错。
Error:Invalid target for nutl#on;must be a DOM node or evented object
TypeError: The element or ID supplied is not valid. (videojs)
关于 videojs,实际项目用到的比较多,坑也是真的坑。文档不太好找,搜索查询了好长时间,才摸索出一套可行的解决方案。面对这类问题,需要善用搜索,从别人的文章和问答中寻找解决问题的思路和方案。查阅官方文档也是个不错的选择,但并不是每个类库框架的官方文档写的都易于理解。videojs 新版的文档和旧版本有些区别,很多 API 看起来并不十分直观,所以版本问题也要注意下。
以上是我解决这个问题的经验分享,欢迎评论区交流。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。