3

我有一个使用https://github.com/selz/plyr媒体播放器的 React 组件。在组件卸载之前一切正常,这会导致 Vimeo API 出错。具体来说:Uncaught (in promise) TypeError: Cannot read property 'postMessage' of null

发生此错误后,我尝试再次加载该模块,它会因为this.player存在而失败undefined,但如果您将其销毁,然后再试一次,它将加载。也许 React Tree 正在保存组件的第一次迭代,我需要以某种方式完全销毁它?

这是我的组件:

import React, {Component, PropTypes} from 'react';
import {findDOMNode} from 'react-dom';
import plyr from 'plyr';

/**
 * @desc Regex to retrieve the Vimeo video ID from the URL.
 * @type {RegExp}
 */
const regex = /^.*(vimeo\.com\/)((channels\/[A-z]+\/)|(groups\/[A-z]+\/videos\/))?([0-9]+)/g;

export default class Vimeo extends Component {

  static propTypes = {
    url: PropTypes.string.isRequired,
  };

  constructor(props) {
    super(props);
    // Use regex to get the video id from the url
    this.videoId = regex.exec(this.props.url);
  }

  /*
   |--------------------------------------------------------------------------
   | Digest Cycle
   |--------------------------------------------------------------------------
   */

  /**
   * @desc Initiate video player.
   */
  componentDidMount() {
    this.player = plyr.setup(findDOMNode(this), {
      controls: [],
      autoplay: true,
      loop: true,
      mute: true,
    });
    this.player[0].on('ready', () => {
      // Mute the video
      if (!this.player[0].isMuted()) {
        this.player[0].toggleMute();
      }
    });
  }

  /**
   * @desc Destroy video player
   */
  componentWillUnmount() {
    this.player[0].destroy();
  }

  /*
   |--------------------------------------------------------------------------
   | Render
   |--------------------------------------------------------------------------
   */

  render() {
    let player = null;
    if (typeof this.videoId !== 'undefined' && this.videoId !== null) {
      player = (
        <div>
          <div
            data-type="vimeo"
            data-video-id={this.videoId}
          />
        </div>
      );
    }

    return player;
  }
}
4

2 回答 2

1

您的问题是由 Plyr 对象被破坏的方式引起的。看一下_destroy()方法,它是 Plyr api 中公开的。

https://github.com/Selz/plyr/blob/master/src/js/plyr.js#L3234

如您所见,在 Vimeo 的情况下,这一行plyr.embed.unload().then(cleanUp);和以下调用setTimeout明确表示_destroy不是同步调用。

首先,plyr.embed.unload()调用这个:

https://github.com/vimeo/player.js/blob/e0f607196f7b38e3a9891e70dda38da2731cff79/src/player.js#L440

它返回一个Promise。一旦 promise 被实现,cleanupPlyr 中的函数就会被执行,这就是 DOM 被更新和变量被重置的时候。这意味着您的组件将在不等待 Plyr 实例完全恢复为默认值或“销毁”的情况下卸载。如果您立即重新安装组件,这可能会导致意外行为。

在里面componentWillUnmount,我建议你不要依赖destroy()更新 DOM 和重置 Plyr 实例。您可以改为删除包装视频嵌入的 DOM 元素,然后将其设置this.player为,null这样内部的 Plyr 实例就会被 gc 销毁并收集。下次你的组件被挂载时,保存视频的 DOM 元素将不存在,你的播放器将正常初始化,设置一个新的 Plyr 实例并再次创建必要的 html。

于 2017-01-11T04:35:18.370 回答
1

您可以使用ReactTransitionGroup低级 API 来访问新的生命周期,componentWillLeave.

npm i -S react-addons-transition-group

通过调用提供的回调,它将允许您在卸载组件之前等待播放器被销毁。200ms 的超时应该足够了,但是如果你想安全的话可以增加它。

所以摆脱你的componentWillUnmount并用类似的东西替换它

componentWillLeave(cb) {
  this.player[0].destroy();
  setTimeout(cb, 200);
}

您必须将您的Vimeo组件包装成一个ReactTransitionGroup,但如果您不喜欢它,您当然可以创建一个包装器,它将在其他地方重用。


此外,您可能想查看react-plyr。它和你有同样的问题,而且很新,但它有测试,结构很好,支持更多的选项。如果你有时间,一起努力使它成为一个很酷的图书馆可能是个好主意;)

于 2017-01-11T06:53:05.417 回答