# LIFFプラグイン
## LIFFプラグインとは
LIFFプラグインとは、LIFF SDKを拡張できる機能です。LIFFプラグインを使うと、LIFF SDKに独自のAPIを追加したり、LIFF APIの挙動を変更したりできます。
LIFFプラグインの実態は、特定のプロパティやメソッドを持つ、オブジェクトあるいはクラスです。
## LIFFプラグインの動作環境
LIFFプラグインはLIFF v2.19.0以降で利用できます。
## LIFFプラグインを使用する
LIFFプラグインは、[`liff.use()`](https://developers.line.biz/ja/reference/liff/#use)メソッドを使用して有効化します。[`liff.use()`](https://developers.line.biz/ja/reference/liff/#use)メソッドにLIFFプラグインを渡すと、LIFFプラグインが有効化されます。LIFFプラグインを有効化すると、`liff`オブジェクトが拡張され、LIFFプラグインのAPIを利用できるようになります。
以下は、LIFFプラグイン`GreetPlugin`を有効化し、`liff.$greet.hello()`メソッドを実行する例です。
### LIFFプラグインがクラスの場合
LIFFプラグインがクラスの場合、[`liff.use()`](https://developers.line.biz/ja/reference/liff/#use)メソッドにインスタンスを渡す必要があります。
```js
class GreetPlugin {
constructor() {
this.name = "greet";
}
install() {
return {
hello: this.hello,
};
}
hello() {
console.log("Hello, World!");
}
}
liff.use(new GreetPlugin());
liff.$greet.hello(); // Hello, World!
liff
.init({
liffId: "123456-abcedfg", // Use own liffId
})
.then(() => {
// ...
});
```
### LIFFプラグインがオブジェクトの場合
```js
const hello = () => {
console.log("Hello, World!");
};
const greetPlugin = {
name: "greet",
install() {
return {
hello,
};
},
};
liff.use(greetPlugin);
liff.$greet.hello(); // Hello, World!
liff
.init({
liffId: "123456-abcedfg", // Use own liffId
})
.then(() => {
// ...
});
```
このように、LIFFプラグインを有効化すると、`name`プロパティの値に`$`の接頭語がついたプロパティが`liff`オブジェクトに追加されます。これにより、LIFFプラグインのAPIを`liff.${LIFFプラグインのnameプロパティの値}.{プロパティ}`や`liff.${LIFFプラグインのnameプロパティの値}.{メソッド}()`の形式で利用できます。
## LIFFプラグインを作成する
LIFFプラグインは、[`name`](https://developers.line.biz/ja/docs/liff/liff-plugin/#name)プロパティと[`install()`](https://developers.line.biz/ja/docs/liff/liff-plugin/#install)メソッドを持つ、オブジェクトあるいはクラスとして作成できます。
以下は、APIとして`hello()`メソッドと`goodbye()`メソッドを提供する、LIFFプラグイン`GreetPlugin`の例です。
### LIFFプラグインがクラスの場合
```js
class GreetPlugin {
constructor() {
this.name = "greet";
}
install() {
return {
hello: this.hello,
goodbye: this.goodbye,
};
}
hello() {
console.log("Hello, World!");
}
goodbye() {
console.log("Goodbye, World!");
}
}
liff.use(new GreetPlugin());
liff.$greet.hello(); // Hello, World!
liff.$greet.goodbye(); // Goodbye, World!
```
### LIFFプラグインがオブジェクトの場合
```js
const hello = () => {
console.log("Hello, World!");
};
const goodbye = () => {
console.log("Goodbye, World!");
};
const greetPlugin = {
name: "greet",
install() {
return {
hello,
goodbye,
};
},
};
liff.use(greetPlugin);
liff.$greet.hello(); // Hello, World!
liff.$greet.goodbye(); // Goodbye, World!
```
### nameプロパティ
`name`プロパティは、LIFFプラグインの名前です。文字列で指定します。
指定した値は、`liff.${LIFFプラグインのnameプロパティの値}`のように、`liff`オブジェクトのプロパティ名となります。
### install()メソッド
`install()`メソッドは、以下のことを行う関数です。
- [LIFFプラグインの初期化処理を記述する](https://developers.line.biz/ja/docs/liff/liff-plugin/#describe-initialization-process-for-liff-plugin)
- [LIFFプラグインのAPIを定義する](https://developers.line.biz/ja/docs/liff/liff-plugin/#define-liff-plugin-api)
#### LIFFプラグインの初期化処理を記述する
`install()`メソッドは、LIFFプラグインの有効化時に[`liff.use()`](https://developers.line.biz/ja/reference/liff/#use)メソッドによって実行されます。そのため、LIFFプラグインの初期化処理を`install()`メソッド内に記述できます。
#### LIFFプラグインのAPIを定義する
LIFFプラグインのAPIは、`install()`メソッドの返り値として定義されます。オブジェクトを返すことで、複数のAPIを定義できます。
なお、LIFFプラグインのAPIが1つのみの場合、そのAPIを返り値とすることも可能です。以下は、`install()`メソッドの返り値として、オブジェクトではなく関数を返す例です。
```js
class GreetPlugin {
constructor() {
this.name = "greet";
}
install() {
return this.hello;
}
hello() {
console.log("Hello, World!");
}
}
liff.use(new GreetPlugin());
liff.$greet(); // Hello, World!
```
#### install()メソッドの引数
`install()`メソッドは、第1引数に[`context`](https://developers.line.biz/ja/docs/liff/liff-plugin/#context)オブジェクト、第2引数に[`option`](https://developers.line.biz/ja/docs/liff/liff-plugin/#option)を取ります。
```js
class GreetPlugin {
constructor() {
this.name = "greet";
}
install(context, option) {}
}
```
##### `context`オブジェクト
`install()`メソッドの第1引数です。以下の2つのプロパティを持ちます。
| プロパティ | 値 |
| --- | --- |
| `liff` | `liff`オブジェクト |
| `hooks` | [フックにコールバックを登録する](https://developers.line.biz/ja/docs/liff/liff-plugin/#register-callback-with-hook)ためのメソッドを提供するオブジェクト |
##### `option`
`install()`メソッドの第2引数です。[`liff.use()`](https://developers.line.biz/ja/reference/liff/#use)メソッドの第2引数に指定した値が渡されます。[`liff.use()`](https://developers.line.biz/ja/reference/liff/#use)メソッドの第2引数が未指定の場合、`option`の値は`undefined`となります。
`option`は、[`liff.use()`](https://developers.line.biz/ja/reference/liff/#use)メソッド側からLIFFプラグインの挙動をカスタマイズできるようにする、といった用途に利用できます。
## フックについて
フックとは、LIFF APIの処理中の特定のタイミングで、事前に登録されたコールバックを実行させるためのLIFFプラグインの仕組みです。フックは、JavaScriptのイベント処理と同じように考えることができます。フックにコールバックを登録しておくと、フックが発火したタイミングで、コールバックが実行されます。
LIFF APIが提供するフックを利用するだけでなく、LIFFプラグインが独自のフックを提供することも可能です。
### LIFF APIのフック
現時点では、LIFF APIは[`liff.init()`](https://developers.line.biz/ja/reference/liff/#initialize-liff-app)メソッドのみ、フックを提供しています。
| LIFF API |
フック |
フックの種類 |
フックの発火タイミング |
liff.init()メソッド |
beforeフック |
非同期フック |
liff.init()の呼び出し直後(LIFFアプリの初期化前) |
afterフック |
非同期フック |
successCallbackの呼び出し直前(LIFFアプリの初期化後) |
### フックの種類
フックには[同期フック](https://developers.line.biz/ja/docs/liff/liff-plugin/#sync-hook)と[非同期フック](https://developers.line.biz/ja/docs/liff/liff-plugin/#async-hook)の2種類があります。
#### 同期フック
同期フックは、登録されたコールバックを同期的に処理します。登録されたコールバックは、登録された順番に処理されます。登録されたコールバックの返り値は無視されます。
#### 非同期フック
非同期フックは、登録されたコールバックを非同期的に処理します。登録されたコールバックは、`Promise.all()`メソッドを使って、並列に処理されます。登録されたコールバックの返り値は`Promise`オブジェクトである必要があります。
### フックにコールバックを登録する
フックにコールバックを登録するには、[`install()`](https://developers.line.biz/ja/docs/liff/liff-plugin/#install)メソッドの[`context.hooks`](https://developers.line.biz/ja/docs/liff/liff-plugin/#context)プロパティを使います。
以下は、[`liff.init()`](https://developers.line.biz/ja/reference/liff/#initialize-liff-app)メソッドの`before`フックと`after`フックにコールバックを登録する例です。[`liff.init()`](https://developers.line.biz/ja/reference/liff/#initialize-liff-app)メソッドが実行されると、`before`フックと`after`フックが発火し、登録されたコールバックが実行されます。
`before`フックと`after`フックは[非同期フック](https://developers.line.biz/ja/docs/liff/liff-plugin/#async-hook)のため、`Promise`オブジェクトを返す必要がある点に注意してください。
```js
class GreetPlugin {
constructor() {
this.name = "greet";
}
install(context) {
context.hooks.init.before(this.initBefore);
context.hooks.init.after(this.initAfter);
return {
hello: this.hello,
goodbye: this.goodbye,
};
}
hello() {
console.log("Hello, World!");
}
goodbye() {
console.log("Goodbye, World!");
}
initBefore() {
console.log("before hook is called");
return Promise.resolve();
}
initAfter() {
console.log("after hook is called");
return Promise.resolve();
}
}
liff.use(new GreetPlugin());
liff
.init({
liffId: "123456-abcedfg", // Use own liffId
})
.then(() => {
// ...
});
```
### フックを作成する
フックは、`SyncHook`クラスあるいは`AsyncHook`クラスのインスタンスとして作成できます。
| フックの種類 | クラス |
| --- | --- |
| 同期フック | `SyncHook` |
| 非同期フック | `AsyncHook` |
以下は、`helloBefore`フックと`helloAfter`フックを作成する例です。`SyncHook`クラスと`AsyncHook`クラスは、`@liff/hooks`パッケージからインポートする必要がある点に注意してください。
作成したフックを発火するには、`SyncHook`クラスのインスタンスや`AsyncHook`クラスのインスタンスの[`call()`](https://developers.line.biz/ja/docs/liff/liff-plugin/#call)メソッドを実行します。
```js
import { SyncHook, AsyncHook } from "@liff/hooks";
class GreetPlugin {
constructor() {
this.name = "greet";
this.hooks = {
helloBefore: new SyncHook(),
helloAfter: new AsyncHook(),
};
}
install(context) {
return {
hello: this.hello.bind(this),
goodbye: this.goodbye,
};
}
hello() {
this.hooks.helloBefore.call();
console.log("Hello, World!");
this.hooks.helloAfter.call();
}
goodbye() {
console.log("Goodbye, World!");
}
}
```
作成したフックは、別のLIFFプラグインがコールバックを登録するのに利用できます。以下は、LIFFプラグイン`GreetPlugin`の`helloBefore`フックと`helloAfter`フックにコールバックを登録する例です。
```js
import { SyncHook, AsyncHook } from "@liff/hooks";
class GreetPlugin {
constructor() {
this.name = "greet";
this.hooks = {
helloBefore: new SyncHook(),
helloAfter: new AsyncHook(),
};
}
install(context) {
return {
hello: this.hello.bind(this),
goodbye: this.goodbye,
};
}
hello() {
this.hooks.helloBefore.call();
console.log("Hello, World!");
this.hooks.helloAfter.call();
}
goodbye() {
console.log("Goodbye, World!");
}
}
class OtherPlugin {
constructor() {
this.name = "other";
}
install(context) {
context.hooks.$greet.helloBefore(this.greetBefore);
context.hooks.$greet.helloAfter(this.greetAfter);
}
greetBefore() {
console.log("helloBefore hook is called");
}
greetAfter() {
console.log("helloAfter hook is called");
return Promise.resolve();
}
}
liff.use(new GreetPlugin());
liff.use(new OtherPlugin());
liff.$greet.hello();
// helloBefore hook is called
// Hello, World!
// helloAfter hook is called
```
#### `call()`メソッド
`call()`メソッドはフックを発火するための関数です。`call()`メソッドには、任意の数の引数を渡すことができます。`call()`メソッドに渡した引数は、フックに登録したコールバックが引数として受け取ることができます。
以下は、フックの`call()`メソッドに引数を渡し、それをコールバックが受け取る例です。
```js
import { SyncHook, AsyncHook } from "@liff/hooks";
class GreetPlugin {
constructor() {
this.name = "greet";
this.hooks = {
helloBefore: new SyncHook(),
helloAfter: new AsyncHook(),
};
}
install(context) {
return {
hello: this.hello.bind(this),
goodbye: this.goodbye,
};
}
hello() {
this.hooks.helloBefore.call("foo");
console.log("Hello, World!");
this.hooks.helloAfter.call("foo", 0);
}
goodbye() {
console.log("Goodbye, World!");
}
}
class OtherPlugin {
constructor() {
this.name = "other";
}
install(context) {
context.hooks.$greet.helloBefore(this.greetBefore);
context.hooks.$greet.helloAfter(this.greetAfter);
}
greetBefore(foo) {
console.log(foo); // foo
}
greetAfter(foo, bar) {
console.log(foo, bar); // foo 0
return Promise.resolve();
}
}
liff.use(new GreetPlugin());
liff.use(new OtherPlugin());
liff.$greet.hello(); // Hello, World!
```
## 公式LIFFプラグイン
以下の公式LIFFプラグインを公開しています。
- [LIFF Inspector](https://developers.line.biz/ja/docs/liff/liff-plugin/#liff-inspector)
- [LIFF Mock](https://developers.line.biz/ja/docs/liff/liff-plugin/#liff-mock)
### LIFF Inspector
LIFF Inspectorは、LIFFアプリをデバッグするためのLIFFプラグインです。LIFF Inspectorを使うと、LIFFアプリを実行している端末とは別のPC上の[Chrome DevTools](https://developer.chrome.com/docs/devtools/)を使って、LIFFアプリをデバッグできます。
LIFF Inspectorについて詳しくは、GitHubの[README](https://github.com/line/liff-inspector/blob/main/README_ja.md)や[npm](https://www.npmjs.com/package/@line/liff-inspector)の[**Readme**]タブを参照してください。
- [GitHub](https://github.com/line/liff-inspector)
- [npm](https://www.npmjs.com/package/@line/liff-inspector)
### LIFF Mock
LIFF Mockは、LIFFアプリのテストを簡単にするためのLIFFプラグインです。LIFF Mockを使うと、LIFF SDKにモックモードを追加できます。モックモードでは、LIFFアプリがLIFFサーバーから独立し、LIFF APIがモックデータを返すため、単体テストや負荷テストをより簡単に行うことができます。
LIFF Mockについて詳しくは、GitHubの[README](https://github.com/line/liff-mock/blob/main/README.md)や[npm](https://www.npmjs.com/package/@line/liff-mock)の[**Readme**]タブを参照してください。
- [GitHub](https://github.com/line/liff-mock)
- [npm](https://www.npmjs.com/package/@line/liff-mock)