SvelteKit + ipx + unpicで動的で最適な画像配信
はじめに
こんにちは、株式会社Liquitousのエンジニアの西村と申します。
本日はSvelte Advent Calendar 2023の7日目ということで、ipxとUnpicを用いた、SvelteKitアプリケーションから呼び出す画像の動的な最適化方法について解説します。
SvelteKitのセットアップ
今回の検証はNode.js 20.10.0、pnpm 8.11.0を使用しています。
- プロジェクトの作成
npm create svelte@latest sveltekit-ipx-unpic
設定(今回は何でも動くと思いますが)
Skeletonプロジェクトを選択し、TypeScriptを使用します。オプションはESLint, Prettierを選択しました。
create-svelte version 5.3.3
┌ Welcome to SvelteKit!
│
◇ Which Svelte app template?
│ Skeleton project
│
◇ Add type checking with TypeScript?
│ Yes, using TypeScript syntax
│
◇ Select additional options (use arrow keys/space bar)
│ Add ESLint for code linting, Add Prettier for code formatting, Try the Svelte 5 preview (unstable!)
│
└ Your project is ready!
次に、作成したプロジェクトのディレクトリに移動します。
cd sveltekit-ipx-unpic
- アダプターをNodeに切り替える
pnpm add @sveltejs/adapter-node -D
+import adapter from '@sveltejs/adapter-node';
-import adapter from '@sveltejs/adapter-auto';
- Expressサーバーを準備し、その上でSvelteKitを動かす
pnpm add express
import { handler } from './build/handler.js';
import express from 'express';
const app = express();
app.use(handler);
app.listen(3000, () => {
console.log('listening on port 3000');
});
"start": "node ./server.js",
- ビルドし、動作を確認する
pnpm build
pnpm start
これでポート3000でSvelteKitが起動します。
ipxの紹介
ipxは、sharpやsvgoを使って画像の最適化を簡単に行うことができます。
UnJSというNuxtのチームが開発しているプロジェクトの一部で、Nuxt ImageやNetlifyでも使用されています。
ipxは単体でサーバーとして立ち上げることも可能ですが、今回はExpressのミドルウェアとして使用します。
ipxをExpressのミドルウェアとして設定
pnpm add ipx
import { createIPX, ipxHttpStorage, createIPXNodeServer } from 'ipx';
// 今回は例にpicsum.photosを使っていますが、実際には自分が提供する画像のホストを指定します。
const imageHost = "picsum.photos";
const storage = ipxHttpStorage({
domains: [imageHost]
});
const ipx = createIPX({
storage,
httpStorage: storage,
alias: {
'/image': `https://${imageHost}`
},
});
export default createIPXNodeServer(ipx);
import { handler } from './build/handler.js';
import express from 'express';
+import ipxServer from './ipxServer.js'
const app = express();
+// パスの先頭が/_ipxの場合にIPXの処理を行うようにします。
+app.use('/_ipx', createIPXNodeServer(ipx));
app.use(handler);
app.listen(3000, () => {
console.log('listening on port 3000');
});
ipxをViteのプラグインとしても設定
Expressのミドルウェアのままだと開発サーバーではipxを使えないため、Viteのプラグインとしても動作するように設定します。
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';
+import ipxServer from './ipxServer';
+import type { Plugin, ViteDevServer } from 'vite';
+
+const ipxPlugin: Plugin = {
+ name: "ipx",
+ apply: "serve", // 開発時のみ有効
+ configureServer(server: ViteDevServer) {
+ server.middlewares.use((req, res, next) => {
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+ // @ts-ignore
+ if (req.url.startsWith('/_ipx')) {
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+ // @ts-ignore
+ req.url = req.url.replace('/_ipx', '');
+ return ipxServer(req, res);
+ }
+ next();
+ })
+ },
+}
export default defineConfig({
plugins: [
sveltekit(),
+ ipxPlugin
]
});
動作確認
それでは、一度サーバーを立ち上げてみましょう。
pnpm dev
-
例えば、
https://picsum.photos/id/20/3670/2462
の画像をipxで縦横400pxにリサイズして取得するには、ブラウザで次のURLにアクセスします。
http://localhost:5173/_ipx/s_400x400/https://picsum.photos/id/20/3670/2462
-
エイリアスを設定しているため、次のURLにアクセスしても同様の結果を得られます。
http://localhost:5173/_ipx/s_400x400/image/id/20/3670/2462
-
リサイズに合わせて画像のフォーマット変換やブラー処理などを入れるには、カンマ区切りで修飾子を追加します。
http://localhost:5173/_ipx/s_400x400,f_webp,blur_5/image/id/20/3670/2462
その他のオプションについては以下のリンクから確認できます。
これで簡単に画像を変換することはできましたが、毎回オプションを見ながらURLをいじるのは面倒です。そこで、もう1つライブラリを紹介します。
Unpicの紹介
unpic-imgは、様々な画像CDNから提供される画像変換APIを、サービス間の差を意識せずに、一貫したインターフェースを通して扱えるようにするライブラリです。
CloudinaryやImgixをはじめ多くのCDNに対応している他、上記でご紹介したipxもサポートしています。
このセクションでは、unpic-imgのSvelte用コンポーネントである@unpic/svelteを使っていきます。
@unpic/svelteの導入
pnpm add @unpic/svelte
<script lang="ts">
import { Image } from "@unpic/svelte";
</script>
<Image
src="/image/id/20/3670/2462"
layout="constrained"
width={800}
height={600}
alt="デザイナーのおしゃれな机"
/>
これでhttp://localhost:5173
にアクセスすると画像が一枚表示されます。この時画像のURLを確認すると、http://localhost:5173/_ipx/s_640x480/image/id/20/3670/2462
のようにオプションや_ipx
といったprefixが追加されているのが分かります。
unpicコンポーネントの詳細な使い方についてはまた後日まとめます。一旦はここ読んどくとokってページを貼っておきます。
終わりに
以上が、動的な画像を配信してSvelteで表示する仕組みの作り方です。
また、本プロジェクトのレポジトリはこちらですので良ければご参考ください。
最後に告知です。12月8日にSvelte Japan Online Meetup #1が開催されます!
オンラインからご視聴いただけるのでぜひご登録ください。
私も、SvelteKitをプロダクション投入した話について発表する予定です📣
Discussion