LoginSignup
10
5

More than 1 year has passed since last update.

liff.init は Vue の createApp より前で実行しよう

Last updated at Posted at 2021-10-11

前提

Vue.js (3.x 系)で liff アプリを作成するとき、以下のように liff.init していました。

RootPage.vue
<template>
  <router-view />
</template>

<script lang="ts">
import liff from '@line/liff/dist/lib';
import { defineComponent, onMounted } from 'vue';
import { useRouter } from 'vue-router';

export default defineComponent({
  name: 'RootPage',
  setup() {
    const router = useRouter();

    onMounted(async () => {
      await liff.init({ liffId: 'xxxxxxxxx' });

      if (!liff.isLoggedIn()) {
        await router.push({
          name: 'page1',
        });

      } else {
        await router.push({
          name: 'page2',
        });
      }
    });
  },
});
</script>

通常の動作はこれで問題ありませんが、クエリパラメータを付与して liff アプリに遷移したときに問題が起こりました。

期待していた遷移の挙動

まず liff アプリに下記のようにクエリパラメータを付与してアクセスします。

https://liff.line.me/xxxxxxxxxxxxx?hoge=hoge&fuga=fuga

すると、liff サーバによってエンドポイントに指定していたアドレスにリダイレクトされます。この時、もともと付与されていたクエリパラメータは URL エンコードされた状態で liff.state というパラメータ内に保持されます。これは、liff アプリにおける1次リダイレクトに相当します。(詳細は公式ドキュメントを参照してください。)

https://myliffapp.com/index.html?liff.state=%3Fhoge%3Dhoge%26fuga%3Dfuga

その後、Vue が起動し、前述した liff.init が呼ばれます。このとき、内部的にliff.stateパラメータを通常のクエリパラメータとして再解釈した URL にリダイレクトされます。これが、2次リダイレクトに相当するものです。

https://myliffapp.com/index.html?hoge=hoge&fuga=fuga

実際の挙動

実際にはliff.initの後、下記のような URL に遷移されてしまいました。

https://myliffapp.com/index.html?hoge

なぜか第1パラメータの hoge のキーだけが残る謎挙動。頭を抱えました。

仮説

VueRouter と liff.init の両方がパスを管理しようとして衝突しているのではないか、 という仮説を立てました。。
ドキュメントにも下記のように記載があります。

liff.init()メソッドが返すPromiseオブジェクトがresolveする前に、サーバーやフロントエンド側の処理などでURLを変更しないようにしてください。

この仮説に基づき、VueRouter が動作し始める前にliff.init してみました。

結果

下記のように変更したところ、クエリパラメータが期待通りに取れるようになりました。

main.ts
import liff from '@line/liff/dist/lib';
import { createApp } from 'vue';

import App from './App.vue';
import router from './router';

async function main() {

  // VueRouter が動き始める前に liff.init する
  await liff.init({ liffId: 'xxxxxxxx' });

  const app = createApp(App);
  app.use(router);
  app.mount('#app');
}

main();
RootPage.vue
<template>
  <router-view />
</template>

<script lang="ts">
import liff from '@line/liff/dist/lib';
import { defineComponent, onMounted } from 'vue';
import { useRouter } from 'vue-router';

export default defineComponent({
  name: 'Base',
  setup() {
    const router = useRouter();

    onMounted(async () => {
      // ここでは liff.init しない!!
      // await liff.init({ liffId: 'xxxxxxxxx' });

      // この時点で正しく2次リダイレクトされ、クエリパラメータは正常に読み取れる。

      if (!liff.isLoggedIn()) {
        await router.push({
          name: 'page1',
        });

      } else {
        await router.push({
          name: 'page2',
        });
      }
    });
  },
});
</script>

まとめ

ネット上では onMounted 内で liff.init している例が散見されるので、同じ部分でハマっている方も多い気がします。react など他のフレームワークでも同様にパスの制御が衝突してしまう可能性があるので、liff.initのタイミングには注意したほうがよさそうです。

10
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
5