# LIFFプラグイン

# LIFFプラグインとは

LIFFプラグインとは、LIFF SDKを拡張できる機能です。LIFFプラグインを使うと、LIFF SDKに独自のAPIを追加したり、LIFF APIの挙動を変更したりできます。

LIFFプラグインの実態は、特定のプロパティやメソッドを持つ、オブジェクトあるいはクラスです。

# LIFFプラグインの動作環境

LIFFプラグインはLIFF v2.19.0以降で利用できます。

# LIFFプラグインを使用する

LIFFプラグインは、liff.use()メソッドを使用して有効化します。liff.use()メソッドにLIFFプラグインを渡すと、LIFFプラグインが有効化されます。LIFFプラグインを有効化すると、liffオブジェクトが拡張され、LIFFプラグインのAPIを利用できるようになります。

以下は、LIFFプラグインGreetPluginを有効化し、liff.$greet.hello()メソッドを実行する例です。

# LIFFプラグインがクラスの場合

LIFFプラグインがクラスの場合、liff.use()メソッドにインスタンスを渡す必要があります。

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プラグインがオブジェクトの場合

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プロパティとinstall()メソッドを持つ、オブジェクトあるいはクラスとして作成できます。

以下は、APIとしてhello()メソッドとgoodbye()メソッドを提供する、LIFFプラグインGreetPluginの例です。

# LIFFプラグインがクラスの場合

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プラグインがオブジェクトの場合

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プラグインの初期化処理を記述する

install()メソッドは、LIFFプラグインの有効化時にliff.use()メソッドによって実行されます。そのため、LIFFプラグインの初期化処理をinstall()メソッド内に記述できます。

# LIFFプラグインのAPIを定義する

LIFFプラグインのAPIは、install()メソッドの返り値として定義されます。オブジェクトを返すことで、複数のAPIを定義できます。

なお、LIFFプラグインのAPIが1つのみの場合、そのAPIを返り値とすることも可能です。以下は、install()メソッドの返り値として、オブジェクトではなく関数を返す例です。

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オブジェクト、第2引数にoptionを取ります。

class GreetPlugin {
  constructor() {
    this.name = "greet";
  }

  install(context, option) {}
}
# contextオブジェクト

install()メソッドの第1引数です。以下の2つのプロパティを持ちます。

プロパティ
liff liffオブジェクト
hooks フックにコールバックを登録するためのメソッドを提供するオブジェクト
# option

install()メソッドの第2引数です。liff.use()メソッドの第2引数に指定した値が渡されます。liff.use()メソッドの第2引数が未指定の場合、optionの値はundefinedとなります。

optionは、liff.use()メソッド側からLIFFプラグインの挙動をカスタマイズできるようにする、といった用途に利用できます。

# フックについて

フックとは、LIFF APIの処理中の特定のタイミングで、事前に登録されたコールバックを実行させるためのLIFFプラグインの仕組みです。フックは、JavaScriptのイベント処理と同じように考えることができます。フックにコールバックを登録しておくと、フックが発火したタイミングで、コールバックが実行されます。

LIFF APIが提供するフックを利用するだけでなく、LIFFプラグインが独自のフックを提供することも可能です。

# LIFF APIのフック

現時点では、LIFF APIはliff.init()メソッドのみ、フックを提供しています。

LIFF API フック フックの種類 フックの発火タイミング
liff.init()メソッド beforeフック 非同期フック liff.init()の呼び出し直後(LIFFアプリの初期化前)
afterフック 非同期フック successCallbackの呼び出し直前(LIFFアプリの初期化後)

# フックの種類

フックには同期フック非同期フックの2種類があります。

# 同期フック

同期フックは、登録されたコールバックを同期的に処理します。登録されたコールバックは、登録された順番に処理されます。登録されたコールバックの返り値は無視されます。

# 非同期フック

非同期フックは、登録されたコールバックを非同期的に処理します。登録されたコールバックは、Promise.all()メソッドを使って、並列に処理されます。登録されたコールバックの返り値はPromiseオブジェクトである必要があります。

# フックにコールバックを登録する

フックにコールバックを登録するには、install()メソッドのcontext.hooksプロパティを使います。

以下は、liff.init()メソッドのbeforeフックとafterフックにコールバックを登録する例です。liff.init()メソッドが実行されると、beforeフックとafterフックが発火し、登録されたコールバックが実行されます。

beforeフックとafterフックは非同期フックのため、Promiseオブジェクトを返す必要がある点に注意してください。

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()メソッドを実行します。

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プラグインGreetPluginhelloBeforeフックとhelloAfterフックにコールバックを登録する例です。

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()メソッドに引数を渡し、それをコールバックが受け取る例です。

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プラグイン

LINEでは、以下の公式LIFFプラグインを公開しています。

# LIFF Inspector

LIFF Inspectorは、LIFFアプリをデバッグするためのLIFFプラグインです。LIFF Inspectorを使うと、LIFFアプリを実行している端末とは別のPC上のChrome DevTools (opens new window)を使って、LIFFアプリをデバッグできます。

LIFF Inspectorについて詳しくは、GitHubのREADME (opens new window)npm (opens new window)の[Readme]タブを参照してください。

# LIFF Mock

LIFF Mockは、LIFFアプリのテストを簡単にするためのLIFFプラグインです。LIFF Mockを使うと、LIFF SDKにモックモードを追加できます。モックモードでは、LIFFアプリがLIFFサーバーから独立し、LIFF APIがモックデータを返すため、単体テストや負荷テストをより簡単に行うことができます。

LIFF Mockについて詳しくは、GitHubのREADME (opens new window)npm (opens new window)の[Readme]タブを参照してください。