この記事は Svelte Advent Calendar 2020 14日目の記事です。
Svelte いいですよね。このサイトも Svelte で構築しています。
軽いし、実装は楽だし、とても気に入ってます。
Svelte の特徴は、ググったらたくさん出てくると思いますが、なんと言ってもコンパイラである事だと思います。コンパイルすると、ライブラリコードがほぼ無い状態の JavaScript と CSS が作られますので、あとはそれを HTML で読み込めば完成です。このサイトを構築している JS も約80kb程度しかありません。
しかし Svelte で SSR をするには Sapper or SvelteKit などの追加モジュールのインストールが推奨になっています。
が、なんとなく Svelte だけでさくっと最低限でも SSR をやりたかったので、実装してみました。 その方法のお伝えできればと思います。
実装したいもの
Svelte を使用しつつ SSR で head 内の title や meta を生成する。
とりあえず Svelte で HELLO WORLD! をやってみる
公式サイトに npx が用意されていますので、まずはそれを実行してみましょう。
$ npx degit sveltejs/template my-svelte-project
$ cd my-svelte-project
$ npm install
$ npm run dev
これで Svelte の HELLO WORLD! が完成しました。publicフォルダの index.html を読み込んでいるだけで、動いています。
SSR での HTML コードも見てみましょう。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width,initial-scale=1'>
<title>Svelte app</title>
<link rel='icon' type='image/png' href='/favicon.png'>
<link rel='stylesheet' href='/global.css'>
<link rel='stylesheet' href='/build/bundle.css'>
<script defer src='/build/bundle.js'></script>
</head>
<body>
</body>
</html>
body の中身は Google のクローラーがちゃんと見てくれると信じて meta タグを SSR しましょう。 meta がないと Twitter などに URL を貼ってもサムネなどが出てこなくて寂しいですからね。
root ディレクトリに app.js を作成し Express.js をインストールしましょう。
$ npm i express
app.js
const express = require('express')
const fs = require('fs')
const app = express()
// static
app.use(`/static`, express.static(`${__dirname}/public/static`, { maxAge: 300 }))
app.use(`/build`, express.static(`${__dirname}/public/build`, { maxAge: 300 }))
// svelte
app.get('*', async (req, res) => {
const metas = []
metas.push(`<meta name="description" content="descriptionだよー" />`)
metas.push(`<meta property="og:url" content="urlだよー" />`)
const dom = fs.readFileSync(`${__dirname}/public/index.html`)
res.send(dom.toString().replace('<head>', `<head>\n ${metas.join('\n ')}`))
})
app.listen(5000, () => {
console.log('App listening on port 5000')
})
これで、先程と同様に public フォルダの index.html を読み込んでいるだけのサイトが出来ました。
再度 SSR での HTML コードも見てみましょう。
$ node app.js
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="description" content="descriptionだよー" />
<meta property="og:url" content="urlだよー" />
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width,initial-scale=1'>
<title>Svelte app</title>
<link rel='icon' type='image/png' href='/favicon.png'>
<link rel='stylesheet' href='/global.css'>
<link rel='stylesheet' href='/build/bundle.css'>
<script defer src='/build/bundle.js'></script>
</head>
<body>
</body>
</html>
無事に meta が SSR されました!完成です!
何をやっているの?
index.html を返す前に、文字列置換で head タグ内を編集しています。
終わりに
正直、ルーティングがない 公式の sample だと「で?」という内容ですが Svelte でちゃんとルーティングがあるサイトを作ろうとすると、この問題に引っかかると思います。
つまりは index.html だけで 複数のページを持つサイトを表現しようと思う時に、この小技が使えると思います。その際は Express.js の req.originalUrl などを見て、動的に meta を書き換える処理を書きましょう。
よかったら、このサイトの SSR での HTML も覗いて見てください。
以上、いい Svelte ライフを!