插件的使用

chimee 是一个简单的框架。如果需要让 chimee 发挥更强的能力,我们需要插件。本章将会介绍如何使用插件。

什么是插件

插件是 chimee 中的一部分,他主要用于解耦业务逻辑,方便功能模块化。它具有以下特性。

如果你想详细了解插件,可以阅读 plugin api

如果你想了解如何编写插件,可以阅读如何编写一个插件

安装插件

在使用插件前,我们需要安装他。那样可以让我们多个 chimee 实例生成对应的插件实例。

安装方法十分简单,我们调用 Chimee 的静态方法 install 即可。

安装后我们可以使用 hasInstalled 方法进行检验。

import popup from 'chimee-plugin-popup';
import Chimee from 'chimee'
Chimee.install(popup({
  name: 'ccPopup',
  title: '这是一个居中信息框',
  body: '这里是信息内容',
  offset: '50% 50%',
  width: '200px'
}));
Chimee.hasInstalled(popup.name); // true

使用插件

安装了插件之后我们就可以使用它了。

声明式调用

我们可以在新建实例时进行声明式调用。

import Chimee from 'chimee';
import ChimeePluginControlbar from 'chimee-plugin-controlbar';

// 安装插件
Chimee.install(ChimeeControlbar);
const player = new Chimee({
  wrapper: '#wrapper',
  src: 'http://cdn.toxicjohann.com/lostStar.mp4',
  autoplay: true,
  // 使用插件
  plugin: [
    ChimeePluginControlbar.name // 或者 'chimeeControl'
  ],
});

这样子这个实例就会使用 chimee-plugin-controlbar 这个插件。

动态调用

我们也可以在实例化后动态调用插件。

下面我们将演示如何动态使用一个中间弹窗组建。

import Chimee from 'chimee';
import chimeePluginPopup from 'chimee-plugin-popup';

Chimee.install(chimeePluginPopup({
  name: 'cc_popup',
  title: '这是一个居中信息框',
  body: '这里是信息内容',
  offset: '50% 50%',
  width: '200px',
}));

const player = new Chimee({
  src: 'http://cdn.toxicjohann.com/%E4%BA%8E%E6%98%AF.mp4',
  wrapper: '#wrapper',
  autoplay: true,
  controls: true,
  muted: true,
});

setTimeout(() => {
  player.use('cc_popup');
}, 2000);

效果如下,我们可以看到两秒后,弹窗动态出现。

传入配置

部分插件自身也会有对应的配置,我们可以在使用的时候通过一个对象参数传入。

import Chimee from 'chimee';
import ChimeePluginDanmu from 'chimee-plugin-danmu';

// 安装插件
Chimee.install(chimeeDanmu);
const player = new Chimee({
  // ...
  // 使用插件
  plugin: [{
    name: ChimeePluginDanmu.name,
    mode: 'canvas',
  }],
});

停用及卸载

我们可以使用 unuse 停用插件,并使用 uninstall 卸载之。

import Chimee from 'chimee';
import ChimeePluginControlbar from 'chimee-plugin-controlbar';

// 安装插件
Chimee.install(ChimeeControlbar);
const player = new Chimee({
  wrapper: '#wrapper',
  src: 'http://cdn.toxicjohann.com/lostStar.mp4',
  autoplay: true,
  // 使用插件
  plugin: [
    ChimeePluginControlbar.name // 或者 'chimeeControl'
  ],
});

// 停用插件
player.unuse(ChimeePluginControllbar.name);
chimee.uninstall(ChimeePluginControllbar.name);

如果我们未停用插件就卸载插件。正在使用插件的实例不会受影响,但是卸载后新建的实例无法使用此插件。

获取插件

安装的插件我们可以通过 $plugins 直接获得。

import Chimee from 'chimee';
import ChimeePluginControlbar from 'chimee-plugin-controlbar';

// 安装插件
Chimee.install(ChimeeControlbar);
const player = new Chimee({
  wrapper: '#wrapper',
  src: 'http://cdn.toxicjohann.com/lostStar.mp4',
  autoplay: true,
  // 使用插件
  plugin: [
    ChimeePluginControlbar.name // 或者 'chimeeControl'
  ],
});

const chimeePluginControllbar = play.$plugins[ChimeePluginControlbar.name];

其中插件的是以对象的方式存储,我们可以通过其 id 获取。

id 一般是 name 的驼峰形式。

id 会在 use 的时候返回。

插件的使用顺序问题

我们可以看到在声明式使用插件的时候,我们传入的是一个数组。数组中插件的顺序与后期的插件的一些顺序也有关。

层级顺序

用户越早安装的插件,层级越低。我们用一个例子体会一下。

import Chimee from 'chimee';
import chimeePluginPopup from 'chimee-plugin-popup';

Chimee.install(chimeePluginPopup({
  name: 'cc_popup',
  title: '第一个信息框',
  body: '我是第一个信息框',
  offset: '60% 50%',
  width: '200px',
}));

Chimee.install(chimeePluginPopup({
  name: 'cc_popup_2',
  title: '第二个信息框',
  body: '我是第二个信息框',
  offset: '50% 50%',
  width: '300px',
}));

const player = new Chimee({
  src: 'http://cdn.toxicjohann.com/%E4%BA%8E%E6%98%AF.mp4',
  wrapper: '#wrapper',
  plugin: [ 'cc_popup', 'cc_popup_2' ],
  autoplay: true,
  controls: true,
  muted: true,
});

我们执行这段代码,可以看到第二个信息框叠在第一个信息框上。这证明第二个信息框的层次比第一个信息框要高。

那么假如我们第二个信息框是动态添加的呢?我们修改一下代码。

import Chimee from 'chimee';
import chimeePluginPopup from 'chimee-plugin-popup';

Chimee.install(chimeePluginPopup({
  name: 'cc_popup',
  title: '第一个信息框',
  body: '我是第一个信息框',
  offset: '60% 50%',
  width: '200px',
}));

Chimee.install(chimeePluginPopup({
  name: 'cc_popup_2',
  title: '第二个信息框',
  body: '我是第二个信息框',
  offset: '50% 50%',
  width: '300px',
}));

const player = new Chimee({
  src: 'http://cdn.toxicjohann.com/%E4%BA%8E%E6%98%AF.mp4',
  wrapper: '#wrapper',
  plugin: [ 'cc_popup' ],
  autoplay: true,
  controls: true,
  muted: true,
});

setTimeout(() => {
  player.use('cc_popup_2');
}, 2000);

我们可以看到第二个信息框依然覆盖在第一个信息框上。

这是因为动态添加的插件比声明式添加的插件顺序靠后,所以他们的层次相对较高。

事件分发顺序

chimee 的 plugin 具有事件拦截功能,所以事件分发的顺序也尤其重要。我们继续利用 popup 进行模拟。

首先我们先模拟正常行为。

import Chimee from 'chimee';
import chimeePluginPopup from 'chimee-plugin-popup';

Chimee.install(chimeePluginPopup({
  name: 'cc_popup',
  title: '第一个信息框',
  body: '我是第一个信息框',
  offset: '60% 50%',
  width: '200px',
  events: {
    pause() {
      this.close();
    },
  },
}));

Chimee.install(chimeePluginPopup({
  name: 'cc_popup_2',
  title: '第二个信息框',
  body: '我是第二个信息框',
  offset: '50% 50%',
  width: '300px',
  events: {
    pause() {
      this.close();
    },
  },
}));

const player = new Chimee({
  src: 'http://cdn.toxicjohann.com/%E4%BA%8E%E6%98%AF.mp4',
  wrapper: '#wrapper',
  plugin: [ 'cc_popup', 'cc_popup_2' ],
  autoplay: true,
  controls: true,
  muted: true,
});

以上代码会在暂停的时候关闭弹窗。

下面我们再修改一下代码,在第一个弹窗插件中对 pause 事件进行拦截。

import Chimee from 'chimee';
import chimeePluginPopup from 'chimee-plugin-popup';

Chimee.install(chimeePluginPopup({
  name: 'cc_popup',
  title: '第一个信息框',
  body: '我是第一个信息框',
  offset: '60% 50%',
  width: '200px',
  events: {
    pause() {
      this.close();
      // 插件中的拦截机制
      return false;
    },
  },
}));

Chimee.install(chimeePluginPopup({
  name: 'cc_popup_2',
  title: '第二个信息框',
  body: '我是第二个信息框',
  offset: '50% 50%',
  width: '300px',
  events: {
    pause() {
      this.close();
    },
  },
}));

const player = new Chimee({
  src: 'http://cdn.toxicjohann.com/%E4%BA%8E%E6%98%AF.mp4',
  wrapper: '#wrapper',
  plugin: [ 'cc_popup', 'cc_popup_2' ],
  autoplay: true,
  controls: true,
  muted: true,
});

我们可以看到第一个弹窗关闭了,但是第二个弹窗并没有消失。这时候我们可以假设第一个插件拦截了第二个插件的暂停事件。

为了验证我们的假设是否正确,我们这次在第二个弹窗处拦截事件。

import Chimee from 'chimee';
import chimeePluginPopup from 'chimee-plugin-popup';

Chimee.install(chimeePluginPopup({
  name: 'cc_popup',
  title: '第一个信息框',
  body: '我是第一个信息框',
  offset: '60% 50%',
  width: '200px',
  events: {
    pause() {
      this.close();
      // 插件中的拦截机制
      return false;
    },
  },
}));

Chimee.install(chimeePluginPopup({
  name: 'cc_popup_2',
  title: '第二个信息框',
  body: '我是第二个信息框',
  offset: '50% 50%',
  width: '300px',
  events: {
    pause() {
      this.close();
    },
  },
}));

const player = new Chimee({
  src: 'http://cdn.toxicjohann.com/%E4%BA%8E%E6%98%AF.mp4',
  wrapper: '#wrapper',
  plugin: [ 'cc_popup', 'cc_popup_2' ],
  autoplay: true,
  controls: true,
  muted: true,
});

可以看到此次两个弹窗同时消失了。换言之,第一个插件的优先级更高,事件更加优先传递到他的手上。

而事实上,事件确实是按照插件安装的顺序派发。越早安装的插件会越早接触到事件。

因此我们鼓励将诸如广告插件等需要具有拦截功能的插件优先安装。

使用 level 值调整层级

但是上述两条规则有一条悖论,如果我又希望我的插件具有优先权,但又希望他附着在最上层。怎么办?

此时我们可以使用 level 这个选项。在我们安装插件后,level值较高者的层级位置永远在level值较低者之上。

依旧是那两个弹窗,我们希望第一个弹窗能够出现在第二个弹窗之上。那么我们可以在安装的时候赋予其 level 值。

import Chimee from 'chimee';
import chimeePluginPopup from 'chimee-plugin-popup';

Chimee.install(chimeePluginPopup({
  name: 'cc_popup',
  title: '第一个信息框',
  body: '我是第一个信息框',
  offset: '60% 50%',
  width: '200px',
  level: 1,
  events: {
    pause() {
      this.close();
      return false;
    },
  },
}));

Chimee.install(chimeePluginPopup({
  name: 'cc_popup_2',
  title: '第二个信息框',
  body: '我是第二个信息框',
  offset: '50% 50%',
  width: '300px',
  events: {
    pause() {
      this.close();
    },
  },
}));

const player = new Chimee({
  src: 'http://cdn.toxicjohann.com/%E4%BA%8E%E6%98%AF.mp4',
  wrapper: '#wrapper',
  plugin: [ 'cc_popup', 'cc_popup_2' ],
  autoplay: true,
  controls: true,
  muted: true,
});

我们可以看到此时第一个弹窗的层级在第二个弹窗上了。

动态调整层级

刚刚举的例子都是静态声明层级,那么如果我们有动态需求怎么实现呢?

我们可以使用 $bumpToTop 来实现我们的需求,$bumpToTo 是插件上的方法,它可以将任意插件置顶。

我们可以使用以下示例代码。

import Chimee from 'chimee';
import chimeePluginPopup from 'chimee-plugin-popup';

Chimee.install(chimeePluginPopup({
  name: 'cc_popup',
  title: '第一个信息框',
  body: '我是第一个信息框',
  offset: '60% 50%',
  width: '200px',
  events: {
    pause() {
      this.$bumpToTop();
    },
  },
}));

Chimee.install(chimeePluginPopup({
  name: 'cc_popup_2',
  title: '第二个信息框',
  body: '我是第二个信息框',
  offset: '50% 50%',
  width: '300px',
}));

const player = new Chimee({
  src: 'http://cdn.toxicjohann.com/%E4%BA%8E%E6%98%AF.mp4',
  wrapper: '#wrapper',
  plugin: [ 'cc_popup', 'cc_popup_2' ],
  autoplay: true,
  controls: true,
  muted: true,
});

我们可以看到第一个弹窗在暂停后置顶了。

当然我们也可以直接获取插件实例进行操作。

import Chimee from 'chimee';
import chimeePluginPopup from 'chimee-plugin-popup';

Chimee.install(chimeePluginPopup({
  name: 'cc_popup',
  title: '第一个信息框',
  body: '我是第一个信息框',
  offset: '60% 50%',
  width: '200px',
}));

Chimee.install(chimeePluginPopup({
  name: 'cc_popup_2',
  title: '第二个信息框',
  body: '我是第二个信息框',
  offset: '50% 50%',
  width: '300px',
}));

const player = new Chimee({
  src: 'http://cdn.toxicjohann.com/%E4%BA%8E%E6%98%AF.mp4',
  wrapper: '#wrapper',
  plugin: [ 'cc_popup', 'cc_popup_2' ],
  autoplay: true,
  controls: true,
  muted: true,
});

setTimeout(() => {
  player.$plugins.ccPopup.$bumpToTop();
}, 2000);

获取所有插件的顺序

我们可以通过 $pluginOrder 获取所有插件的顺序列表。其中列表中储存的为插件 id 。