NuxtでInstagram記事の埋め込みを行う

## やったこと
既存の静的サイトをNuxtでSSGしたかったので移行していた。
その際にinstagramの通常の埋め込みコードをそのままtemplateに記述してみていた。
↓一部省略したコード


```html
<blockquote class="instagram-media" data-instgrm-permalink="https://www.instagram.com/p/[記事のID]/" data-instgrm-version="12" style=" background:"......>
</blockquote> <script async src="//www.instagram.com/embed.js"></script>
```

## 起こったこと
ページの読み込み時に表示されたりされなかったりと、不安定な状態に。

## 原因
instagramの埋め込みコードは`<blockquote>`をJavaScriptで`<iframe>`に置換する仕様（SNSの埋め込みコードの多くは似たような感じ）なので、Nuxtのライフサイクルとタイミングが合わない場合に要素が取得できていないようで、`<blockquote>`のままになってしまうことがあった。

## 対策その1
要素が取得出来ていないのなら`<blockquote>`直後の`embed.js`をmounted内で実行すれば拾えそうなので以下のようにしてみる。

```vue
<template>
  <blockquote class="instagram-media" data-instgrm-permalink="https://www.instagram.com/p/...">
  </blockquote>
</template>

<script>
export default {
  mounted () {
    const script = document.createElement('script')
    script.src = '//www.instagram.com/embed.js'
    this.$el.appendChild(script)
  }
}
</script>
```

ロード時の不安定な挙動は解消された。
しかしながらnuxt-linkでの遷移後に再訪問すると100%表示されなくなってしまった。
ただ100%再現される事象なので一歩前進。

## ~~対策その2~~

**追記 (2019/12/26)**
対策その2のInstagram APIは2020年初めに廃止になるそうなので断念…
ログとして残しておきます。
https://www.instagram.com/developer/

~~対策その1のコードでは`embed.js`の実行タイミングが管理できないのでAPIを使う。
https://www.instagram.com/developer/embedding/#oembed~~

> **OMITSCRIPT**
> If set to true, the embed code does not include the script tag. This is useful for websites that want to handle the loading of the embeds.js script by themselves. To manually initialize the embed code, call the JS function instgrm.Embeds.process().

~~パラメータを見ていると、ロード処理を自分で処理できるというまさに今やりたいことが可能な夢の機能を発見したので早速使ってみる。~~

```vue
<template>
  <div v-html="html" />
</template>

<script>
export default {
  data () {
    return {
      html: ''
    }
  },
  mounted () {
    this.loadEmbedJS()
    this.getInstagram('http://instagr.am/p/[記事のID]/')
  },
  methods: {
    getInstagram (url) {
      fetch('https://api.instagram.com/oembed?url=' + url + '&omitscript=true')
        .then((res) => {
          return res.json()
        })
        .then((json) => {
          this.html = json.html
          this.$nextTick(() => {
            window.instgrm.Embeds.process()
          })
        })
    },
    loadEmbedJS () {
      const script = document.createElement('script')
      script.async = script.defer = true
      script.src = '//www.instagram.com/embed.js'
      this.$el.appendChild(script)
    }
  }
}
</script>
```

~~無事安定して表示されるように！
`$nextTick`の中でないと`window.instgrm`が取得できないところに注意が必要。
あとはv-htmlを使うならsanitizeもちゃんとした方が良いかも。。~~

## 対策その3
対策その1でnuxt-link遷移後に表示されなかったのは、一度`embed.js`を読み込むと`window.instgrm`ができ、nuxt-linkで遷移しても破棄されないため。
`//www.instagram.com/embed.js`の中身を覗いてみると以下の記述があるので、
再訪問しても`window.instgrm`が既に存在することで実行されていない可能性が高い。

```javascript
window.instgrm||(window.instgrm={Embeds:{}})
```

上記を踏まえて`beforeDestroy`で`window.instgrm`を`delete`してみる。

```vue
<template>
  <blockquote class="instagram-media" data-instgrm-permalink="https://www.instagram.com/p/...">
  </blockquote>
</template>

<script>
export default {
  mounted () {
    this.loadEmbedJS()
  },
  beforeDestroy () {
    delete window.instgrm
  },
  methods: {
    loadEmbedJS () {
      const script = document.createElement('script')
      script.async = script.defer = true
      script.src = '//www.instagram.com/embed.js'
      this.$el.appendChild(script)
    }
  }
}
</script>
```

表示は問題なくされるようになった！
ただ再訪問時に以下のスクリプトエラーを吐くようになってしまった。。

```
Uncaught Error: Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.
```

minifiedされたJSなので例外の発生のみの警告。
フルメッセージを見るにはnon-minifiedなJSを使え、とのこと。
それは難しいのでどうしたものか。

同様のエラーに見舞われている方の記事を発見したものの、
`//platform.instagram.com/en_US/embeds.js`は現在では
`//www.instagram.com/embed.js`と同じソースにたどり着くようなので
解決には至らず。。
[instagramの埋め込みコードがエラーを吐く件]
(https://hagishiri.net/web-design/424/)

表示と挙動には影響が見受けられなかったので、不本意ながらエラー処理は一旦保留。

## おわり
現状はエラー解決まで至らなかったですが、表示だけでもAPIを経由してだいぶ遠回りするはめになったので、本記事が少しでもお役に立てば幸いです。
また間違いや改善点がありましたらご指摘いただけると嬉しいです！