OGP画像の動的生成系アプリを作るとき用メモ Router編【Vue.js+firebase】

Vue.jsとfirebaseでの爆速開発に興味があったので実際にやってみました。
ついでにSPAのOGP画像動的生成もしたわけですが、意外と苦戦したのでメモを残します。
いくつかの記事を参考にし開発しましたが、ルーティング部分（今回は、Vueアプリとfirebase functionsでいろいろ操作する）が個人的に難しかったのでそこを中心に書きます。

*OGP画像とはtwitterなどにシェアする際、文字やリンクだけではなく画像も一緒につぶやくためのもの。
Peing（質問箱）が有名。


ここから下には
1. 開発したプチサービスの紹介
2. 参考にした記事
3. ルーティングに関するメモ
という具合に進めます。

#開発したプチサービスの紹介
今回、「言の葉メーカー」というWebアプリを作りました。
自分が創った言葉をTwitterで拡散するためのアプリです。

(追記：既に停止しているサービスです)


##少々コンセプトを
日本語の発展速度は凄まじく、毎年のように新しい言葉は生まれています。
近年で言えば、インターネット用語が生まれたり、それが一般化したり...名詞に「る」をつけ無理やり動詞化したり。若者言葉なんてものもあります。
これらの言葉を作るのは、どこかの企業や団体なのか個人なのかは様々でしょうが、せっかく良いヒラメキがあったにもかかわらず、埋もれてしまった言葉は多数あることでしょう。

それを少しでも目立つようにできたらいいなと思ってこのアプリを作ってみました。
別のアプローチもできそうですがとりあえずこれで...:mask:

#参考にした記事
[「Vue.jsとFirebaseでOGP画像生成系のサービスを爆速で作ろう」を実際に作ってみる](https://qiita.com/taishikato/items/6cbcdc8fe5103084c737)
基本的なアプリの仕組みはこちらを参考にしました。

[SNS映えするWebアプリを...！FirebaseとVue.jsでSPAのOGP画像の動的生成をやってみたら案外楽だった](https://qiita.com/yuneco/items/5e526464939082862f5d)
自分用にカスタマイズする際にこちらも参考にしました。

検索で出てくる、OGP動的生成アプリを作るための記事は、これらを引用していることが多いように感じます。この通りにしたら基礎はできます。
ですので、この記事では詳細な実装手順というよりルーティングについてのちょっとした補足のみを記します。
ほとんど自分の備忘録です:expressionless:

#ルーティングに関するメモ
Vue.jsで作るようなSPA（シングルページアプリケーション）では、OGPのためのmetaタグを動的に反映することができません。
なぜならTwitter側から読み込む際、JSを解釈してくれないからです。

この問題を解決するための方法の一つとして
**FirebaseのHostingとFunctionsを利用する**手があります。

###仕組み
1. OGPのmetaタグを動的に生成したいパス（Twitterにシェアするリンク）にアクセスするとFunctionが起動するようにHostingで設定
2. Functionでmetaタグを生成しHTMLを上書きする。このときscript内にVueアプリ側のパスへ飛ばす記述をしておく

すると...

3.**Twitter**：scriptを解釈しないのでVueアプリは起動せずOGPが読み込まれる
　**ユーザ**：scriptが解釈され、パスの書き換えが行われるのでVueアプリが起動する

###実装のためのコード（画面遷移を中心に）
ここからはユーザの視点に立って、アプリの作成例として記します。

####画像を作成するユーザの行動
まず、ユーザはOGP画像のためのデータを生成するでしょう。
今回の私のアプリでは、ユーザのフォーム入力から画像を生成しています。
なので、例えばボタンをクリックしたら諸々の処理をして、生成した画像のプレビュー画面あるいはTwitterから飛んできた人が見る画面(`/share/:id`)へ遷移するようにします。

```Create.vue
<v-btn @click="create">
//略

<script>
export defalut{
   methods: {
     create(){
         //画像の生成や保存の処理などもろもろ
         this.$router.push({ path: `/share/${this.uuid}` });
     };
   };
}
</script>
```
```router.js
routes: [
    {
      path: "/share/:id",
      component: Share
   },
```

プレビュー画面にきたユーザは作成した画像をシェアします。
:exclamation:ここでツイートに載せるURLを、現在いる`/share/:id`ではなくfunctionを呼び出すための`/twshare/:id`にします

```Share.vue
<v-btn @click="Tweet">
//略
<script>
export default{
   methods: {
     Tweet() {
      const id = this.$route.params.id;
      const url = `https://twitter.com/intent/tweet&hashtags=言の葉メーカー&url=https://<domain>/twshare/${id}`;
      const option = "status=1,width=818,height=400,top=100,left=100";
      window.open(url, option);
    }
   };
}
</script>
```

こうしてTwitterは`/twshare/:id`のページへアクセスしfunctionが起動します。
functionはOGP画像のmetaタグなどのhtmlを上書きします。

↓`twshare/*`のURLでfunctionが起動するように。

```firebase.json
"hosting": {
    "public": "dist",
    "ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
    "rewrites": [
      {
        "source": "/twshare/**",
        "function": "tw"
      },
      {
        "source": "**",
        "destination": "/index.html"
      }
    ]
  }
```
functionの処理については省略。上記した参考通りに。
上書きするHTMLは以下のようにします。
metaタグを必要なものにし、scriptでは本来ユーザに見て欲しいページである`share/:id`に飛ばします。

```vue:index.js
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>言の葉メーカー</title>
    <meta property="og:title" content="${TITLE}">
    <meta property="og:image" content="${imageurl}">
    <meta property="og:image:width" content="${OGP_IMG_WIDTH}">
    <meta property="og:image:height" content="${OGP_IMG_HEIGHT}">
    <meta property="og:description" content="${DESCRIPTION}">
    <meta property="og:url" content="${PAGEURL}">
    <meta property="og:type" content="article">
    <meta property="og:site_name" content="言の葉メーカー">
    <meta name="twitter:site" content="${SITEURL}">
    <meta name="twitter:card" content="summary_large_image">
    <meta name="twitter:title" content="${TITLE}">
    <meta name="twitter:image" content="${imageurl}">
    <meta name="twitter:description" content="${DESCRIPTION}">
  </head>
  <body>
    <script type="text/javascript">window.location="/share/${id}";</script>
  </body>
</html>
```

これで画像を作成するユーザの行動は終わりです。
OGP画像付きのツイートができました。

####シェアされたツイートを見たユーザの行動
続いて、そのツイートを見たユーザの行動として...
ツイートからサイトにアクセスすることになります。
ツイートした`/twshare/:id`のページを開きfunctionが起動、`index.js`に書いた通りにscriptが読み込まれ`/share/:id`へ飛ばされます。
飛ばされると同時にVueアプリが起動(firebase.jsonでいう↓の部分)

```json
{
        "source": "**",
        "destination": "/index.html"
}
```
よって全ての行動が終了です。

#まとめ
以上になります。
この仕組みを基盤として様々なジャンルのアプリに応用が効くと考えるとワクワクしますね:relaxed:

「言の葉メーカー」
まだまだ改善が必要なものですが、少しづつ進めていきます。