ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Vue2集成海康威视无插件开发包:从单画面到多画面预览实战

2026/10/3 18:02:36 拓冰建站 浏览量
Vue2集成海康威视无插件开发包:从单画面到多画面预览实战 在 Vue2 项目里接海康威视摄像头预览绝大多数人第一反应是去找海康威视 WEB 无插件开发包 V3.4然后翻开官方 demo 照着抄。我也一样但真正上手之后才发现坑比想象中多开发包版本怎么选、JS 文件放哪个目录、浏览器为什么一直黑屏、Vue2 生命周期和插件初始化顺序怎么配合、多画面预览时 CPU 为什么直接飙到 90%。这篇文章把我从下载开发包到最终在 Vue2 工程里实现 1/4/9/16 多画面预览的完整过程整理出来代码可以直接抄踩过的坑也一并列清楚。如果你正准备在一个 Vue2 项目里对接海康设备这篇应该能帮你省下至少两天时间。就算你项目用的是 Vue3核心逻辑其实也通用只是挂载和销毁的写法需要调整。1. 需求拆解与方案选型1.1 为什么必须用“无插件”方案先明确一个背景海康威视摄像头的网页预览早期主要依赖 ActiveX、NPAPI 这类浏览器控件用过的人都知道必须在 IE 或者老版本 Chrome 里安装一个专用插件才能看画面。后来 Chrome 全面禁用了 NPAPIEdge 和 Firefox 也陆续不支持 ActiveX这条路基本被堵死了。所以海康才推出了 WEB 无插件开发包。注意这里“无插件”的意思是浏览器端不需要安装任何本地控件视频流通过 WebSocket 等方式推到浏览器里播放。V3.4 这个版本目前仍然在很多老项目里服役因为功能成熟、接口稳定尤其是需要做 1/4/9/16 多画面预览的监控大屏项目用它要比自己拼 RTSP 转 HLS 再对接播放器省事得多。1.2 Vue2 项目集成的三条主流路径我在接这个需求之前先盘了一下 Web 端集成海康视频的常见路子大致有三条。第一条把官方 demo 直接改造成 Vue 组件。这是最常规的做法适合单页内只需要一块视频墙的场景。缺点是官方 demo 里的代码通常是一大坨原生 JS 写在一起塞进 Vue 组件后需要自己处理生命周期和事件回调代码显得不够清爽。第二条封装成通用插件或者指令。把 JSPlugin 的初始化、播放、销毁、分屏切换这些动作抽成一个独立的 service 或者 mixin业务页面只需要传设备列表和布局参数拿到的就是一个可直接用的视频组件。这条路径后期维护最舒服我在正式项目里就是按这个思路做的。第三条用 iframe 嵌入官方 demo 页面。好处是基本不用写前端代码坏处是跨域通信、样式定制、权限传递都很难受稍微有点交互需求就绕不动只适合临时验证摄像头能不能出画面。我最终选了封装成 Vue2 组件这条路。这样视频墙是独立模块其他页面只需要引入组件然后传数据后续加云台控制、截图、录像也能在组件里集中扩展。1.3 这套方案能做什么、不做什么把话说清楚避免你对接一半才发现方向不对。这套方案能做的是单画面预览、多画面分屏预览、通过回调监听鼠标点击和双击事件、在原子里扩展抓图录像、云台控制。多画面布局可以由插件内置的分屏算法处理也可以自己控制窗口坐标实现不规则布局。不能做的是直接用浏览器播放 RTSP 地址。这点经常有人踩坑RTSP 从来就不是浏览器原生支持的协议无插件开发包本质上是让设备或者平台把码流转换成浏览器能消费的 WebSocket 流再在前端渲染出来。所以你在网上查到各种rtsp://admin:passwordip:554/Streaming/Channels/101可以用 VLC 验证设备取流是否正常但不能直接填到前端播放器里。另外还要明确低版本 IE 是不支持的现代浏览器Chrome 67、Edge、Firefox基本没问题。开发环境如果是 HTTPS 页面需要注意混合内容限制WebSocket 也尽量走 WSS否则会被浏览器拦掉。2. 开发包下载与环境准备2.1 下载入口与文件清单海康威视 WEB 无插件开发包 V3.4 的下载入口一般是在海康官网的下载中心或者开发者社区里搜“WEB无插件开发包”“JSPlugin”关键词认准版本号和更新时间打包下载下来。解压之后目录里通常能看到 demo、doc、src 这几类内容。demo 是官方的前端示例doc 里是接口说明 PDF 或者 CHMsrc 下面就是我们要用到的核心脚本常见的有jquery-1.7.1.min.js、JSPlugin.js有的版本还带videoPlugin.js、webVideoCtrl.js之类具体文件名以你下载的版本为准。拿到手之后先别急着往 Vue2 工程里塞我建议先在本地把官方 demo 跑通。有的版本直接双击 index.html 就能用有的版本受限浏览器安全策略需要起一个本地 HTTP 服务来访问可以用 VSCode 的 Live Server 或者随便一个python -m http.server起服务然后再打开页面。打开 demo 之后填上摄像头的 IP、端口、用户名、密码如果能正常出画面说明你的设备和网络环境是没问题的后续搬到 Vue2 工程里出问题就可以放心排查前端集成的问题而不是去怀疑摄像头配置。2.2 浏览器与设备双侧的环境检查在代码层面动手之前我强烈建议先把环境因素排查一遍否则很容易陷入“代码都没问题但就是不出画面”的困境。浏览器这一侧检查是不是现代浏览器控制台有没有报什么全局错误。设备这一侧需要一个支持 WebSocket 取流的固件版本。很多海康摄像机默认 WebSocket 开关是不开或者不支持的需要在设备的 Web 管理界面里找配置项一般在“配置-网络-高级设置-集成协议”附近把 WebSocket 或 WebSocket 取流勾选上保存重启。这里有个经验如果你用的摄像机比较老固件里根本没有 WebSocket 选项那前端再怎么调也白搭。处理方式通常是升级摄像头固件或者通过后端平台、NVR 中转取流让 nginx 或者流媒体服务把摄像头码流转给前端。项目里如果遇到“明明 demo 里可以换台设备就不行”的情况优先去查设备固件版本和 WebSocket 配置项。网络这一侧开发机和摄像头需要在同一个二层网络里至少网络要互通。如果摄像头部署在公网或者跨网段通常需要把 SDK 端口默认 8000、RTSP 端口默认 554做端口映射还要考虑上行带宽。4G 摄像头或者弱网环境多画面基本会卡成 PPT这是物理限制前端代码解决不了。2.3 将开发包集成进 Vue2 工程官方 demo 跑通之后就该把核心脚本挪到 Vue2 工程里了。我习惯的做法是在项目的public目录下新建一个hik文件夹把jquery-1.7.1.min.js、JSPlugin.js等需要用到的文件拷进去然后在public/index.html的head或者body里通过script标签按顺序引入。注意是“按顺序”jQuery 一定要先于 JSPlugin 加载因为JSPlugin.js内部依赖 jQuery。如果顺序反了控制台会直接报jQuery is not defined。有的项目用的是static目录原理一样Vue2 脚手架会把static下的文件原样拷贝到打包根目录。我个人更推荐放public因为index.html引用路径更直观也不容易被 webpack 处理出奇怪的问题。引入之后在 Vue 组件里通过window.JSPlugin全局访问。因为这些脚本不走 webpack 打包所以不建议在组件里用import引入容易碰到模块作用域和严格模式的问题直接用全局变量最省心。这里踩过一个坑如果项目里已经安装了新版本 jQuery比如 3.x不要试图让老插件去共用它。海康这套老开发包依赖的 jQuery 1.7.1 有一些特有的事件绑定逻辑新版 jQuery 可能不兼容。如果只是项目里其他模块用了新 jQuery而index.html里又全局引入了老 jQuery两者会互相污染。这种情况可以把老 jQuery 塞进 iframe 里跑或者干脆让视频组件独立成一个微前端别跟主项目全局变量混在一起。2.4 先认识几个关键 API这里先把几个核心 API 概念理清楚后面上代码你才不会晕。new JSPlugin()是用来创建播放器实例的构造函数参数里包括容器 id、布局模式、最大分屏数、窗口分割数组等。JS_OpenWindow()用来打开一个视频窗口传入窗口索引和位置尺寸多画面预览时就是循环调用它。JS_PlayStreamByIP()是根据设备 IP、端口、通道号、码流类型去拉流播放这是最常用到的预览接口。JS_SetWindowControlCallback()用来注册窗口事件回调比如单击、双击、键盘事件。停止和销毁则有一组接口常见的有JS_StopRealPlayAll()、JS_HideWindow()你可以打开 demo 源码看它用的是什么名字。这些 API 在官方文档里都有但我建议你以自己下载的 demo 源码为准因为不同小版本的参数个数和命名偶尔会不太一样。3. 单画面预览从 0 到出图3.1 初始化前必须确认 DOM 已经渲染完成在 Vue2 里写视频组件最容易犯的错是在created钩子里就去new JSPlugin()。created阶段 DOM 还没挂载容器都不存在插件自然找不到挂载点。正确做法是在mounted之后等 DOM 渲染完成再初始化。但mounted本身只保证组件挂载了如果容器是在v-if或者弹窗里挂载时机还要再晚一点。所以我习惯在mounted里套一层this.$nextTick()确保容器宽度、高度都已经布局完成再执行初始化。另一个很实际的坑是容器宽高为 0。有些项目在弹窗还没弹出来的时候就初始化播放器弹窗打开后只有一片黑控制台也没报错其实就是因为初始化时容器不可见宽高全是 0插件内部计算布局就乱了。解决办法是先让弹窗显示或者等待容器可见之后再初始化初始化时如果检测到宽高为 0可以直接给一个默认值比如 800x600。3.2 创建播放器实例并播放一路画面以下是单画面预览的完整初始化方法可以直接搬到 Vue2 组件里用initPlayer() { const container document.getElementById(videoContainer) if (!container) return this.player new window.JSPlugin({ szId: videoContainer, iType: 2, iWidth: container.clientWidth, iHeight: container.clientHeight, iMaxSplit: 16, oSplit: [1, 4, 9, 16], oStyle: { borderSelect: #12c2ff, background: #000000 } }) this.player.JS_SetWindowControlCallback({ onPlayerPerform(index, eventType, data) { console.log(窗口事件, index, eventType, data) } }) this.player.JS_OpenWindow(0, 0, 0, 0) this.player.JS_PlayStreamByIP({ ip: 192.168.1.64, iPort: 8000, iChannel: 1, iStreamType: 1, szProtocol: HTTPS, bZeroChannel: false }) }参数逐个说szId是容器 id注意这里传的是字符串不是 DOM 对象或者 Vue 的 ref。iWidth和iHeight是播放器初始化尺寸我用的是容器实际宽高这样插件内部才能正确计算窗口网格。iMaxSplit表示最大支持多少分屏oSplit是对应的分屏数组比如[1, 4, 9, 16]支持 1 分屏、4 分屏、9 分屏、16 分屏。JS_OpenWindow(0, 0, 0, 0)的前两个参数是窗口坐标后两个是宽高传 0 表示让插件按分屏模式自动布局。JS_PlayStreamByIP里的ip是摄像头 IPiPort是 SDK 服务端口海康默认 8000iChannel是通道号iStreamType是码流类型1是主码流2是子码流。szProtocol我用的是HTTPS如果你的设备 Web 服务是 HTTP就对应改成HTTP。bZeroChannel是是否启用零通道编码普通摄像机传false。JS_SetWindowControlCallback里的回调可以拿到窗口索引和事件类型后面做双击放大、窗口交换都要靠它。3.3 用 URL 方式播放与 RTSP 地址对照除了按 IP 播放部分版本还支持JS_PlayStreamByURL直接传一个取流地址。这一步好多人会想直接把 RTSP 填进去其实不一定行得通具体要看这个版本的开发包有没有把 RTSP 地址做协议转换。我建议把 RTSP 地址当作排查工具来用。海康常见的 RTSP 格式如下码流类型地址格式通道 1 主码流rtsp://用户名:密码IP:554/Streaming/Channels/101通道 1 子码流rtsp://用户名:密码IP:554/Streaming/Channels/102通道 2 主码流rtsp://用户名:密码IP:554/Streaming/Channels/201通道 2 子码流rtsp://用户名:密码IP:554/Streaming/Channels/202截图的时候101的 1 是通道号后面的01是码流类型01代表主码流02代表子码流。这个规则很实用写脚本批量验证摄像头状态的时候经常用到。前端出不了画面时用 VLC 打开对应的 RTSP 地址先验证一遍如果 VLC 能出画面说明设备侧没问题问题大概率在网络协议配置或者前端插件初始化上。3.4 播放器实例千万别放进 data这个是 Vue2 老项目里特别容易踩的坑。很多人写组件喜欢把所有变量都放data里结果把播放器实例也放进去然后发现画面卡顿、按钮反应迟钝甚至插件内部状态莫名其妙被改动。原因很简单Vue2 的data会对所有属性做递归响应式代理而JSPlugin实例内部有大量 DOM 引用、浏览器原生对象、定时器回调这些被 Vue 的Object.defineProperty重新包装后性能和稳定性都会有隐患。正确做法是把播放器实例直接挂在this上或者在created里初始化成一个非响应式属性比如this.player null后续赋值也不经过data。组件销毁时再把它置空。这个经验对集成任何第三方 DOM 密集型插件都适用。4. 多画面预览实现4.1 多路设备数据组织多画面预览的核心是把“设备列表”和“窗口索引”对应起来。我建议在组件里维护一个channelList数组每个元素代表一路视频源channelList: [ { id: 1, name: 南门岗亭, ip: 192.168.1.64, port: 8000, channel: 1, streamType: 2 }, { id: 2, name: 北门岗亭, ip: 192.168.1.65, port: 8000, channel: 1, streamType: 2 }, { id: 3, name: 停车场东, ip: 192.168.1.66, port: 8000, channel: 1, streamType: 2 }, { id: 4, name: 停车场西, ip: 192.168.1.67, port: 8000, channel: 1, streamType: 2 } ]注意这里的streamType我写的是 2也就是子码流。多画面预览的场景尤其是 9 分屏、16 分屏主码流会把带宽和 CPU 直接打满子码流分辨率虽然低但用于监控墙足够。单画面或者回放需要高清时再切换主码流。4.2 分屏切换与多窗口播放多画面播放的完整逻辑我封装成三个方法switchSplit负责切换分屏模式playAll负责按当前分屏数播放所有通道stopAll负责停止所有窗口。switchSplit(n) { this.currentSplit n if (this.player.JS_ChangeSplit) { this.player.JS_ChangeSplit(n) } else if (this.player.JS_SetSplit) { this.player.JS_SetSplit(n) } this.stopAll() this.playAll() }, stopAll() { if (this.player.JS_StopRealPlayAll) { this.player.JS_StopRealPlayAll() } }, playAll() { const list this.channelList.slice(0, this.currentSplit) list.forEach((item, index) { this.player.JS_OpenWindow(index, 0, 0, 0) this.player.JS_PlayStreamByIP({ ip: item.ip, iPort: item.port, iChannel: item.channel, iStreamType: item.streamType, szProtocol: HTTPS, bZeroChannel: false }) }) }切换分屏的接口名在不同版本里确实有差异所以代码里做了兼容判断你打开 demo 看它用的是哪个方法名保留一个即可。切换分屏前先停止所有窗口是因为插件内部对已打开的窗口有状态缓存如果直接对已有窗口重新布局容易出现画面串位也就是 0 号窗口播了 3 号通道的画面之类的问题。currentSplit的数据结构是数字模板里按钮可以这样渲染button v-forn in [1, 4, 9, 16] :keyn :class{ active: currentSplit n } clickswitchSplit(n) {{ n }} 分屏 /button4.3 多画面下的性能调优16 路主码流同时预览对前端电脑和网络都是灾难。项目里如果必须同时看 16 路我建议做几件事。第一码流类型统一用子码流。海康子码流一般分辨率在 640x360 左右帧率也低一些16 路同时解码的压力会小很多。第二控制同时真实播放的路数。像 16 分屏里有些通道可能并不需要实时监看可以做成按需播放点击哪一路再拉主码流。第三如果页面长时间挂着建议加一个“轮巡”功能比如每 10 秒自动切换一片通道预览避免一直 16 路拉流。第四组件销毁时一定要停止所有播放并释放资源否则路由跳走后视频流还在后台跑页面会越来越卡。4.4 点击放大与窗口交换多画面里最常见的交互是双击某一画面放大再次双击还原。这个可以用JS_SetWindowControlCallback实现。思路是在回调里拿到当前点击的窗口索引index如果是双击事件就把这个窗口对应的设备信息存到activeChannel然后把当前分屏临时切换成 1 分屏播放activeChannel对应的那一路。还原的时候再切回原来的分屏数重新playAll。事件类型值不同版本不一样有的版本里鼠标双击是某个常量有的直接给字符串所以写代码前先看 demo 里的onPlayerPerform回调打印出来的参数。我项目里是在鼠标事件回调里判断eventType如果是双击且当前分屏数大于 1就走放大逻辑。注意放大后 1 分屏用的是主码流看的更清晰。窗口交换也是类似原理窗口事件里拿到源窗口索引再通过拖拽或者右键菜单触发交换。无插件开发包一般提供窗口内容切换的能力有的是重新调用JS_OpenWindow换一个设备来播有的可以直接交换内部通道。具体接口名字在文档里搜“交换窗口”或者看 demo 的云台控制示例。5. 常见问题与排查技巧5.1 插件加载失败 / JSPlugin is not defined这个报错我见得太多了。出现JSPlugin is not defined十有八九是脚本没有引入成功或者引入顺序不对。排查顺序第一步浏览器控制台切到 Network 面板搜JSPlugin.js和jquery-1.7.1.min.js看请求状态是不是 200。如果 404说明路径不对检查文件是否真的放在public或static下以及index.html里的script路径是否匹配。第二步确认 jQuery 在 JSPlugin 之前加载。第三步确认引入脚本后没有立刻被其他模块覆盖全局变量比如项目里全局执行了window.$ undefined。还有一个隐蔽问题如果是在 Vue 单页应用的某个路由里才用到视频墙而脚本只在index.html引入刷新页面首次加载是没问题的。但如果用了按需加载路由或者组件懒加载脚本被延迟加载可能出现new window.JSPlugin的时候它还没就位。这种情况可以写一个工具函数轮询等待window.JSPlugin存在后再初始化。5.2 一直黑屏没有画面黑屏是最难查的一类问题因为报错不一定明显。按照我的经验按概率从高到低排查这几个原因。第一个设备没有启用 WebSocket 取流。这是最常见的原因登录设备 Web 管理界面找 WebSocket 相关配置启用后重启设备。第二个端口不通。开发机到摄像头 8000 端口如果被防火墙挡了SDK 握手就过不去。用telnet 摄像头IP 8000测试一下能通再继续。第三个szProtocol和实际 Web 服务协议不匹配。设备用的是 HTTP你却传了HTTPS有时就会握手失败改成HTTP试试。第四个码流类型不对。有些球机或者特殊设备的主码流编码格式浏览器端不支持切到子码流反而能出画面。第五个https 页面里加载了 http 的 WebSocket 流浏览器的混合内容安全策略会直接拦掉导致黑屏。开发环境尽量用 http 访问页面生产环境配置 https 反向代理并让 WebSocket 也走 wss。我排查黑屏问题的时候习惯先打开浏览器控制台切换 Sensor 面板看有没有 WebSocket 网络请求如果连请求都没有说明问题在插件初始化或者容器渲染阶段如果请求一直在但画面黑多半是解码或者码流编码问题。5.3 经常断流、卡顿、CPU 占用高多画面预览的场景CPU 占用和网络占用是逃不开的话题。前端这边能优化的是尽量用子码流、降低同时播放的路数、及时发现并停止离屏的画面。后端或者设备侧可以试试把摄像头的子码流编码格式改成 H.264不要用 H.265因为多路 H.265 在浏览器里硬解兼容性差CPU 直接爆炸。断流问题则更复杂常见原因有网络抖动、设备并发路数超限、摄像机自身码流设置过高、NVR 把 IPC 的流踢掉。排查时先看断流的通道是不是集中在同一台 NVR 上如果是基本就是后端并发策略或者带宽瓶颈。如果是随机通道断流建议对 TCP 和 WebSocket 的保活机制做一下处理插件一般有断线重连回调监听之后自动重连一路。5.4 报错速查表报错或现象优先排查方向JSPlugin is not defined脚本未引入或顺序不对jQuery is not definedjQuery 未加载或版本冲突初始化后容器空白容器宽高为 0或 szId 传错黑屏且 WS 连接正常码流编码不支持切换子码流/H.264提示connect failed网络不通检查端口与防火墙提示Device not support websocket设备固件不支持升级固件或走平台中转play failed通道号错误、权限不足、密码错误多画面串流切换分屏前没有停止所有窗口路由跳转后画面还在跑组件销毁时未调用停止接口弹窗内初始化黑屏弹窗不可见时初始化宽高计算为 05.5 几个容易被忽略的“坑”最后说几个不太可能写在官方文档里的经验。弹窗组件里放视频墙一定先让弹窗可见再初始化插件。很多 UI 框架的弹窗默认是懒渲染的你在mounted里初始化时弹窗内容还没有真正插入 DOM等弹窗出来就黑屏。解决办法是监听弹窗打开事件或者用v-if控制视频组件渲染时机确保打开后再nextTick初始化。路由跳转和 keep-alive 的组合也要注意。如果视频墙页面被keep-alive缓存离开页面时不会触发destroyed视频流会一直挂着。这种情况需要配合activated和deactivated钩子在deactivated里停止播放在activated里重新拉流。还有一个小细节多路播放时尽量不要在大屏项目里让浏览器窗口处于最小化状态有些浏览器的后台标签页节流策略会降低 WebSocket 消息处理频率导致画面延迟变大。真机验证时别只看着后台预览窗口最好把页面放到前台再观察延迟。我在实际项目里对接这套开发包最大的体会是一定先把官方 demo 在同网络环境里跑通再动手往 Vue2 工程里搬。很多人一上来就写代码最后发现是设备配置或者环境问题白白浪费一整天。多画面预览这种功能前端只是把窗口和播放流管理清楚真正的瓶颈通常在网络带宽和设备并发能力上前期把环境验证做扎实后面写代码其实是一个很顺的过程。最后再分享一个实用小技巧多画面调试阶段可以在onPlayerPerform回调里把窗口索引和设备信息打印出来对照着就能很快定位画面串位和黑屏的问题比起盲猜参数高效得多。