個人開発界隈ではOGP芸というものが流行っているみたいですね。
SNSにシェアする際に表示されるOGPの設定や画像をいい感じにするみたいなことのことですが、SPAではこれを生成するのが割と面倒だったりします。
自分が作っているg4でもOGPを使ったシェアを実装していますが、SSRを使って配信していて、割と設定が面倒だったりしています。
今回、なるべくSSRをしないでSPAのままやってみることはできないかというのをfly.ioで試してみました。
作ったものの概要
- 実際にOGP画像として表示される画像は
/image.png?i=0
で配信する。 -
/
に?i=0
のようなパラメータをアクセスすると、入力した↑を含むOGPが反映されたhtmlを表示する - fly.ioが日本語に対応してないので、titleに日本語は使えない
- せっかくfly.ioを使ってますが、今回はOGPだけにフォーカスしたいのでキャッシュ周りは実装してません
作ったもの
or
これをtwitterやfacebookなどでシェアしてみてください。
作り方
事前にflyのcliをPCにインストールしておく
npmは必要ないです(あったほうが便利だけど最速なので)
必要なファイルは4つだけ
- index.js: なんか処理書くやつ
- template.html: テンプレートです。ここに情報を流し込む
- fly.yml: なんか設定書くやつ
- sample.svg: OGP芸したい適当なsvg。今回はURLによって内容を書き換えるため雑に{{title}}みたいなやつをここに書いて、テキスト置換でそこに内容を埋め込んでます。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<meta name="theme-color" content="#000000" />
<title>fly.ioで最速ogp芸</title>
<meta name="description" content="by shwld" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:creator" content="@shwld" />
<meta property="og:type" content="website" />
<meta property="og:url" content="https://www.g-g-g-g.games" />
<meta property="og:title" content="shwld" />
<meta property="og:description" content="shwld" />
<meta property="og:image" content="https://www.g-g-g-g.games/assets/image.png" />
<meta property="og:image:alt" content="shwld" />
</head>
<body>
<h1>fly.ioでogp芸をする場合のサンプルだよ</h1>
</body>
</html>
templateはこんな感じになりました。
ogpのcontentは置き換えるので何でもいいです。とりあえずさり気なくg4のものを入れてアピールしておきます。
次にindex.js
です。こちらにすべての処理を書きます。
import { Image } from '@fly/image'
import { mount } from "@fly/fetch/mount"
// templateをテキストで取得するFunction
async function getTemplate() {
const resp = await fetch("file://src/template.html")
return await resp.text()
}
// svgをテキストで取得しつつ中身ちょっと埋め込めるFunction
async function getSvgText(title) {
const resp = await fetch("file://src/sample.svg")
const text = await resp.text()
// g4(https://www.g-g-g-g.games)ではReactでテキスト化されたsvgを吐いてるが、とりあえず単純に置換する
return text.replace('{{content}}', title)
}
// 画像形式のレスポンス作るFunction
async function responseImage(svgText) {
const svgResp = new Response(Buffer.from(svgText))
const buf = await svgResp.arrayBuffer()
const png = new Image(buf).png()
const result = await png.toBuffer()
return new Response(result.data, {
headers: {
'Content-Type': 'image/png',
'Content-Length': result.data.byteLength.toString(),
}
})
}
// 出力データのパターン
const TITLES = [
'g4 is pomodoro rpg!',
'fly.io de OGP!!',
]
// fly.ioのrouterみたいなやつ。ここに処理を書いてく。
const mounts = mount({
// このパスでogpの画像を生成する
'/image.png': async (req, init) => {
const url = new URL(req.url)
// URLからQueryStringを取得
const index = url.searchParams.get('i')
if (index !== '0' && index !== '1') {
return new Response('not found', { status: 404 })
}
// 対応したタイトルを取得
const title = TITLES[index]
// タイトルをsvgに埋め込んだテキストを作る
const svgText = await getSvgText(title)
// svgからpng画像を生成する
return responseImage(svgText)
},
// このパスをシェアする
'/': async (req, init) => {
const url = new URL(req.url)
// URLからQueryStringを取得
const index = url.searchParams.get('i')
if (index !== '0' && index !== '1') {
return new Response('not found', { status: 404 })
}
// 対応したタイトルを取得
const title = TITLES[index]
// テンプレートをparseして編集できるようにする
const doc = Document.parse(await getTemplate())
// テンプレートにOGPを埋め込む
doc.querySelector('meta[name="description"]').setAttribute('content', title)
doc.querySelector('meta[property="og:url"]').setAttribute('content', url.href)
doc.querySelector('meta[property="og:title"]').setAttribute('content', title)
doc.querySelector('meta[property="og:description"]').setAttribute('content', title)
doc.querySelector('meta[property="og:image"]').setAttribute('content', `${url.origin}/image.png?i=${index}`)
doc.querySelector('meta[property="og:image:alt"]').setAttribute('content', title)
// htmlを返す
return new Response(doc.documentElement.outerHTML, {
headers: { 'Content-Type': 'text/html' },
status: 200,
})
},
})
// リクエストをmountsの定義を使って処理するように設定する
fly.http.respondWith(mounts)
index.jsはこんな感じ。
/
はtemplate.html
のogpを書き換えて出すだけ。
/image
はsvgファイルを読み込んで文字列を置換したものをpngに変換して返してます。
fly.ymlやsvgファイルは特に特別な設定はないのでドキュメントや最後にソースを貼るのでそちらを見ていただければ。
あとは、fly.ioのコンソールでアプリを作って、fly deploy
するだけ。
再度になりますが、
or
これをtwitterやfacebookなどでシェアしてみてください。
以上になります。ソースはこちらにあります。