つい先日ですが、著書の特設サイト (下記リンク) を作っているとき、コンテンツの一部として、Twitter でつぶやいた書籍に関連するツイートを埋め込もうと思ったんですね。
ちなみに、こういう簡単なランディングページっぽいものは、Next.js 使っています。よく使うような UI コンポーネントも自前で用意してあるので、簡単なページを短期間でササッと作りたい時にはとても便利。
で、話をツイートの件に戻しますが、今まで Next.js アプリケーションにツイートを埋め込むっていう要件を扱ったことがなかったのでどうしようかなと考えた結果、最初は普通に Twitter 標準の埋め込みツイート機能を使用して終わりでいいやって思っていたんですけども、それで実装してみたあとで、念のためにパフォーマンス計測している時、埋め込みツイート、めちゃくちゃ重てぇってことに気がつきまして・・・・・・ Lighthouse のスコアで Performance 項目が赤く染まったのをみてダメだこりゃってなったわけです。
そこで、そういえば以前、Next.js で静的にツイートを埋め込むデモを見たぞ、ということでメモっておいた 「Static Tweet Next.js Demo」 を確認。ソースコードを拝見して、使わせて頂くことにしました。
元のソースコードだと複数のツイートを読み込む際にちょっと使いにくかったので、その辺を含めて再利用がしやすいように改変しましたが、今回はそれについてまとめてみます。
Next.js アプリケーションでツイートを埋め込む
最も単純にツイートを埋め込むなら、Twitter が標準で提供している 「埋め込みツイート」 の機能を使用すればよいということになります。
公式の埋め込みツイート機能で取得できるソースコードには、ツイートごとに <script>
タグが付いてきますが、これは分離し、_app.js
などで、下記のように読み込んでやればよいので、パフォーマンスとかそういうのをとりあえず置いておいて単に使用するだけならそれ程難しいことではありません。
useEffect(() => { const tweet = document.createElement('script'); tweet.setAttribute('src', 'https://platform.twitter.com/widgets.js'); tweet.setAttribute('defer', 'true'); document.head.appendChild(tweet); }, []);
標準の埋め込みツイート クソ重い問題
これでとりあえず実装してみて気がついたんですけども、冒頭に書いたとおりすげぇ重たい。
で、ちょっと調べてみたら、埋め込みツイート内 (Twitter から読み込まれた JavaScript によって、blockquote
で記述されたツイートのソースコードは、iframe
要素に変換されるんですけども、その iframe
要素内) で、2MB 以上ある JavaScript ファイルが読み込まれてて、これが著しくパフォーマンスを落としていることがわかりました。
embed.vendors~ondemand.Tweet.e1fe983255cfe9803951.js
みたいなファイル名の .js
ファイルなんですが、ファイルサイズがでかい上に、埋め込まれたツイートの各 iframe
要素内で読み込まれるので、簡単にいえばこのサイズのファイルが、埋め込んだツイートの数だけ読み込まれると。そりゃ重たいわ。
さすがにこりゃないわ、ってことで、標準の埋め込みツイートは使用せず、静的にツイートを埋め込む方法で実装しなおすことにしました。
Static Tweet to your next app
まず、今回のソースコードは下記にまとめています。
適当にツイートを埋め込んだデモも下記に用意しておきましたので、あわせてどうぞ。
冒頭にも書いたとおり、ベースとしたのは下記で、Next.js における SSG のデモとして公開されていたソースコードです。
デモ自体は getStaticProps
、getStaticPaths
関数、および getStaticPaths
における fallback
の挙動を紹介するためのものですので、Next.js アプリケーション内に複数のツイートを埋め込むような用途を想定したコードにはなっていません。
できれば、
<Tweet id="[tweet_id]" />
みたいな感じで簡単にツイートを埋め込みたかったので、頑張ってコード書こうって手を付けたんですけども、static-tweet の readme.md
を読んだら、ご親切に最後に 「おめーの Next.js アプリケーションに静的にツイートを埋め込みたい場合はこっちのソースコード参考にしろよ(意訳)」 ってサンプルソースへのリンクが書かれてて、もうこれ、私がやりたかったことそのままじゃんってことでほぼ丸パクリの勢いで参考にさせて頂きました。
オリジナルからの変更点
基本的にベースにさせて頂いたソースコードから必要な部分だけを抜き出しているので、私が書いたオリジナルの要素はほぼないんですけども、いくつか私が使いやすいように改変しました。大したことやってないので、下記に箇条書きにしてまとめておきます。
- 元のソースコードは、パッケージを最新の状態にしたときにツイートが正常に埋め込まれないっていう現象にぶち当たりました。調べてみたところ
remark-parse
のアップデートにより、パーサーが変更された(参考リンク)ことで、Markdown のパースに失敗しているようでしたので、それに対応するため、remark-gfm
パッケージの導入と、それに合わせたソースコードの改変を行っています。 - static-tweet/components/post/tweet.js 内で、
isDark
変数をセットすることで、埋め込まれるツイートのデザインを、「ライトテーマ」、または 「ダークテーマ」 から選択可能にしました。 - システムのテーマカラーでダークテーマを選択している場合は、
prefers-color-scheme
メディアクエリを使用して、自動的に埋め込みツイートのデザインがダークテーマになるようにしてあります。 - その他、気になったところをちょこちょこと細かく修正
実際の使用方法は、pages/index.js
のソースコードを見ていただければすぐわかるレベルだと思います。
Twitter API を経由してデータを取得、静的な HTML としてアプリケーション内に埋め込まれるため、非常に軽いですし、特に複数のツイートを Next.js アプリケーション内に埋め込む場合はパフォーマンスの面で大きく向上させることができると思います。
ちなみに、元のツイートが削除された場合は、ビルド時にデータ取得が失敗することになりますが、逆にいえば再ビルドしない限りは表示され続けることになります (静的に HTML として埋め込まれているので当然ですが)。
この辺、自分のツイートを埋め込む場合は気にする必要ないと思いますが、他人のツイートを埋め込む用途に使用した場合、ツイート主の意思に反して埋め込みツイートが表示され続けることになりますので、気をつけた方がよいと思います。