Vue-cli多页应用YoutubeApi预加载与postMessage错误修复求助
解决YouTube API相关的两个错误
环境信息
- 项目:Vue-cli 开发的多页应用
- 测试浏览器:Google Chrome 103.0.5060.134
- 依赖版本:
vue@^2.6.11、vue-meta@^2.4.0、vue-router@^3.2.0
遇到的错误
- 刷新页面(cmd+shift+R/F5)时触发预加载错误:
The service worker navigation preload request was cancelled before 'preloadResponse' settled. If you intend to use 'preloadResponse', use waitUntil() or respondWith() to wait for the promise to settle.
- 切换页面时触发postMessage错误:
www-widgetapi.js:987 Failed to execute 'postMessage' on 'DOMWindow': The target origin provided ('https://www.youtube.com') does not match the recipient window's origin ('http://localhost:3000').
相关代码实现
Mounted 钩子
... data () { return { player: null } }, mounted () { const tag = document.createElement('script') tag.src = 'https://www.youtube.com/iframe_api' const getHead = document.getElementsByTagName('Head')[0] getHead.append(tag) // 首次初始化youtubeapi window.onYouTubeIframeAPIReady = () => { this.initYoutube(this.$refs.player) } // 再次进入页面时 this.initYoutube(this.$refs.player) }, beforeDestroy () { // 离开页面时销毁播放器 if (this.player !== null && this.player.destroy) { this.player.destroy() delete this.player } },
Methods 方法
methods: { initYoutube () { if (!window.YT) return this.player = new window.YT.Player('player', { height: '1080', width: '1920', videoId: 'HDx8Z-XB7A4', playerVars: { autoplay: 1, controls: 0, disablekb: 1, fs: 0, iv_load_policy: 3, loop: 1, modestbranding: 0, rel: 0, playsinline: 1, playlist: 'HDx8Z-XB7A4', origin: 'http://localhost:3000', // 似乎无效 host: 'https://www.youtube.com'// 似乎无效 }, events: { onReady: this.onPlayerReady, onStateChange: this.onPlayerStateChange } }) }, onPlayerReady (evt) { // 循环播放视频 evt.target.mute() evt.target.playVideo() }, onPlayerStateChange (evt) { // 循环播放视频 if (evt.data === 0) { evt.target.playVideo() } } ... }
解决方案
1. 解决postMessage跨域错误
- 问题根源:重复初始化播放器导致YouTube iframe多次发送跨域消息,且
origin参数硬编码无法适配环境变化。 - 修复步骤:
- 用
window.location.origin动态获取当前页面域名,替换硬编码的origin值,确保和页面实际域名完全一致(无末尾斜杠)。 - 只在
window.YT不存在时加载API脚本,避免重复注入;onYouTubeIframeAPIReady事件仅绑定一次,防止重复触发初始化。 - 用
this.$refs.player代替固定ID创建播放器,避免DOM元素重复或找不到的问题。 - 销毁播放器时将
player设为null而非delete,符合Vue响应式规范。
- 用
2. 解决service worker预加载错误
- 问题根源:Vue-cli开发环境默认启用的PWA service worker,预加载逻辑和YouTube iframe的资源加载冲突,导致请求被取消。
- 修复步骤:
- 本地开发时可直接禁用PWA:在
vue.config.js中添加配置:module.exports = { pwa: { enabled: false // 开发环境禁用PWA } } - 若需保留PWA功能,可在service worker配置中排除HTML页面和YouTube相关资源的预加载:
module.exports = { pwa: { workboxOptions: { exclude: [/\.html$/, /youtube\.com/] } } }
- 本地开发时可直接禁用PWA:在
修改后的代码示例
修改后的Mounted钩子
data () { return { player: null } }, mounted () { // 仅当YT未加载时注入API脚本 if (!window.YT) { const tag = document.createElement('script') tag.src = 'https://www.youtube.com/iframe_api' document.head.append(tag) // 仅绑定一次API就绪事件 window.onYouTubeIframeAPIReady = () => { this.initYoutube() } } else { // YT已加载,直接初始化播放器 this.initYoutube() } }, beforeDestroy () { if (this.player && typeof this.player.destroy === 'function') { this.player.destroy() this.player = null } }
修改后的Methods方法
methods: { initYoutube () { if (!window.YT || !this.$refs.player) return this.player = new window.YT.Player(this.$refs.player, { height: '1080', width: '1920', videoId: 'HDx8Z-XB7A4', playerVars: { autoplay: 1, controls: 0, disablekb: 1, fs: 0, iv_load_policy: 3, loop: 1, modestbranding: 0, rel: 0, playsinline: 1, playlist: 'HDx8Z-XB7A4', origin: window.location.origin // 动态获取当前origin }, events: { onReady: this.onPlayerReady, onStateChange: this.onPlayerStateChange } }) }, onPlayerReady (evt) { evt.target.mute() evt.target.playVideo() }, onPlayerStateChange (evt) { if (evt.data === 0) { evt.target.playVideo() } } ... }
内容的提问来源于stack exchange,提问作者Lynne
相关产品推荐
相关产品推荐

