皆さん、こんにちは。私の名前は Fran Dios です。JavaScript 全般、特に Vue.js が大好きなフルスタックエンジニアです。今日は、一般的なエッジコンピューティングと、エッジで Vue.js を使ってサーバーサイドレンダリングを行う方法について学びます。そのために、3 つの異なる技術を見ていきましょう。
- JavaScript コードをエッジにデプロイするための破壊的なサーバーレスプラットフォームである「Cloudflare Workers」
- Web アプリを開発・構築するための新しいフロントエンドツールである「Vite」
- そして、私が現在取り組んでいる「Vitedge」というフレームワークを使って、エッジでウェブアプリを実行するために、これらをどのように組み合わせるか
私と同じようにこのテーマに興味を持っていただき、トークを楽しんでいただければ幸いです。では、さっそく始めましょう。
まず最初に、文脈を説明します。皆さんは「サーバーレス」という言葉を聞いたことがあると思います。「クラウド機能」や「ラムダ」とも呼ばれています。私が数年前に初めてこの言葉を聞いたときは、自分の作った API が低レイテンシーかつ高いスケーラビリティを持って世界中で動作するようになると想像しました。実際には、この「サーバーレス」は、私が期待していたものの半分しか実現できませんでした。
確かにスケーラブルで、従量制の課金があるのは事実です。しかし、一般的には世界の 1 箇所(通常は米国)でしか稼働しません。
その上、これらのクラウド機能は「コールドスタート」と呼ばれるものに悩まされています。これは基本的に、私たちの関数が実行されてからしばらくすると、サーバーのメモリから削除される可能性があり、次のリクエストでは、サーバーが再びメモリに関数をロードする必要があることを意味しています。このプロセスは、このグラフ(画像参照)でわかるように、ある程度の時間がかかることがあります。AWS:最大で 0.5 秒、GCP:最大で 1.5 秒、Azure:最大で 4 秒です。
それとは別に、クラウドの機能はステートレスであり、そのメモリやデータは、一般的に異なるリクエスト間で共有することができません。これは、メモリリークやその他のエラーを防ぐことができるので、通常は良いことです。しかし、認証やユーザーセッションの保存など、「ステートフル」な環境に比べて作業が複雑になる場合もあります。このような場合には、Redis のような別のサービスが必要になります。
「これまでの」サーバーレス環境とは対照的に、Cloudflare社は少し前にCloudflare Workers(Workers)と呼ばれる新しい破壊的な技術をリリースしました。Workersは、元来私が「サーバーレス」だと考えていたものです。
Workersを使うことで、一度JavaScriptをデプロイすれば世界中の200ヶ所以上のロケーションで動作させることができるようになります。(訳註: アメリカのみならず)アフリカや中国にもノードが存在するのです。
Workersは次の二つの理由でデプロイされた大抵のコードとAPIを高速に動作させます。
- コードはユーザーのすぐ近くで実行されます。例えば私が東京にいる場合、成田にあるノードが私のリクエストを処理します。もし福岡にいる場合はより近い大阪のノードによって処理されることでしょう。これは情報がアメリカに移動して戻ってくる必要がないため、ネットワークのレイテンシが数桁減ることを意味します
- 2つ目の理由は、これまでのサーバーレスプラットフォームとは異なり、Workersにはコールドスタートがないことです。コードの実行を開始するために、文字通りの「0ミリ秒」を必要とします
各ワーカーは「0ミリ秒のコールドスタート」で何百万回も生成される可能性があるため、コードは非常にスケーラブルです。
また、Workersはユーザーの国、都市、さらには郵便番号のような様々なロケーションユーティリティを提供していたりと、多くの可能性に満ちています。 これらのプロパティにアクセスするためにはAPIでリクエストオブジェクトを検証することが必要なだけです。
私がWorkersの魅力を語り尽くしたとあなたは思うかもしれませんが、さらによいものがあります。Workersは加えて以下のものを提供します。
- 柔軟なエッジキャッシュ。APIがリクエストを処理した際にオプションでその結果を保存することができます。そして、次のリクエストでは直接コピーを取得し処理をさらに高速化することができます
- 前にステートレス問題に触れたことを覚えていますか? Workersはステートレスですが、ユーザーセッションや静的ファイル、他必要なものを保存可能な組み込みのグローバルのキーバリューストアにアクセスすることができます
- 最近、彼らはWebSocketのサポートを追加しました。普通であれば、コードの実行後にコネクションが閉じられるためステートレス関数はOPENなWebSocketコネクションを維持できません。しかしながら、Durable Objectsと呼ばれるサイドサービスのおかげで、OPENなWebSocketコネクションをグローバルに保存できるようになりました。これによって、たとえば、リアルタイムのビデオゲームやGoogleドキュメントのようなコラボレーションツールを構築できることを意味します。そして、それらはすべて、エンドユーザーの近くのエッジで実行されるのです
- もちろん、Cloudflareで実行されている場合、Workerはほとんどのサービスと十分に統合されているため、Cloudflareが提供する他のすべてのサービスにもアクセスできます。たとえば、ビルトインのDNS、分析、cronジョブ、セキュリティといったものです。万が一、DDOS攻撃を受けた場合でもCloudflareでボタンを押すだけでレート制限などを適用し、影響を軽減できます
- 大事なことを言い忘れましたが、Workersは通常、「従来の」サーバーレスプラットフォームよりも安価です
さて、ここまでWorkersをご紹介してきましたが、実際にWorkersを使って何を作れるでしょうか?
これらは、ワーカーが一般的に使用される例です。
- A/B テスト。ユーザーの位置情報などに基づいて、異なる情報を返すことができます。
- Anlytics。ある程度までは、SDK をブラウザにプッシュすることなく、Worker から直接、優れたアナリティクスを行うことができます。
- 最近しなければならなかったことがあります。日本のオンラインショップで、海外のお客様にだけバナーを表示することです。日本国内のユーザーには何も表示されませんが、海外のユーザーにはバナーが表示されます。
ここまで、Cloudflare Workersを使った様々なメリットを紹介してきましたが、いくつか欠点もあります。
- Workersは、Node.js APIでなく、Web APIに従う傾向があります。これは、Stripe SDKやAuth0のJWT検証ユーティリティなど、Worker環境でサポートされていないバックエンド用のNPMパッケージが多数あることを意味します。生のAPIを使用するか、Node.js APIの代わりにWeb標準に依存するユーティリティを見つける必要があります
- Workersは非常に制約のある環境で実行されています。そのため、最大メモリ、CPU時間、または実行できるサブリクエストの数などの実行時の制限が発生します。すべてのアプリケーションがこの環境で実行できるわけではありません。アプリを作成するときは、このことを念頭に置く必要があります
- また、DX(Developer eXperience)は他のプロバイダーと比べてまだそれほど優れていないため、デバッグが少し難しくなる可能性があります。ただし、この点は徐々に改善されています
- Workersのコミュニティはまだまだ若いので、特定のことを行う方法を学ぶためにより多くの時間を費やす必要があるかもしれません
ここまで実際の「エッジへのデプロイ」が何を意味するか、をCloudflare Workersを使うことでみてきました。続いては、Viteを見ていきましょう。
Vite という名前について聞いてことがあるかもしれません。Vite は Vue.js の作者 Evan You によって開発された新しいフロントエンドツールです。 Webpack そして Webpack-dev-server を組み合わせたものと考えていいですが、はるかに速く、そして設定も簡単です。
このおかげで、Viteを使ってWebアプリケーションを作成する際のDXはとても良いものになっています。デフォルトの設定値はほとんどのアプリケーションに対応していますし、設定を変更する必要がある場合でも、とても簡単です。例えば、SASSやSCSSを使用する必要がある場合は、それらのコンパイラをインストールするだけで、Viteは自動的にそれらを選択します。
Viteの開発サーバーはほとんど瞬時に開始します。そして、Hot Module Replacement (HMR)も同様です。ファイルを保存するとすぐにブラウザでアップデートが表示されます。
それに加えて、Viteのコミュニティはとても活発です。多くの優秀な開発者が、想像できるすべてのもののためにプラグインを作っています。ファイルシステムのルーティング、コンポーネントの自動インポート、国際化のためのプリコンパイル、アイコンへの簡単なアクセス、などなど。プラグインをインストールして、1行のコードでインポートするだけでいいのです。
Viteはフレームワークに依存がありませんが、もちろん Evan You によって作られているため、Vue.js で完璧に動作することは想像に難くありません。
それとは別に、私が最も気に入っているのは、Viteがとても簡単に拡張できることです。內部関数のほとんどをエクスポートしているので、WebSocketやサーバーサイドレンダリングを扱うツールを簡単に作ることができます。
そして、これこを私がこの数ヶ月間取り組んできたことなのです。Vite SSR ツールをエッジで Cloudflare Workersで実行しています。
そして、これを私は Vitedge と呼んでいますが、Vite アプリケーションを作ってそれを Couldflare Workers にデプロイすると、ページコンポーネントの HTML をエンドユーザーに近いエッジでレンダリングすることができます。複雑に聞こえるかもしれませんが、それはシングルページアプリケーションの開発するとき同じくらい簡単です。
したがって、CDN から静的なindex.html
をアップロードして提供する代わりに、CND ノード自身がindex.html
ファイルをレンダリングし、次のリクエストのためにエッジにキャッシュします。そのため、次のリクエストでは、あたかも静的なファイルであるかのようにファイルを取得します。
おそらく、以下のようないくつかのメリットを想像できます:
- より良いロード時間、遅いビルドなしによる SEO、動的なコンテンツをサポート
- 素晴らしいパフォーマンス
- 構成可能なキャッシュ、つまり、私達の(データベースやコンテンツ管理システムからの)データが特定のルートで変更されたことがわかっている場合は、そのルートをキャッシュから削除し、他のルートはそのままにしておくことができるのです。そのため、そのルートに対する次のリクエストは、新しいデータを使用することになります
- Vitedge はファイルシステムによるルートに基づいた API エントリポイントを作成する方法も提供します
- そして、もちろん、Vite がもたらす良い DX もすべてあります
SSR は多くのアプリケーションに適しているかもしれまんが、特定のプロジェクトにおいては適していないかもしれないということも、私は伝えておきたいと思います。プロジェクトによっては、SPA と静的サイトジェネレーターは良い選択となるでしょう。
それでは、Vite アプリケーションに Vitedge をインストールして使用する方法を見てみましょう。
もし、まだ Vite に慣れていない方は、通常のアプリは次のようになっています。vite.config
ファイルと Vue.js アプリケーション向けの通常の"source"フォルダがあり、ページコンポーネント、ルート、ルートコンポーネント(App)、そして Vue アプリ用のメインエントリファイルがあります。
Vitedge をインストールする方法は、単純にそのプラグインを Vite の設定ファイルに追加し、Vue アプリケーションのエントリポイントを次のように変更します。Vitedge は環境に応じて、createApp
またはcreateSsrApp
を呼び出します。このメインフックでは、i18n、Vuex、Pinia などあらゆる Vue.js モジュールをセットアップすることができます。
デフォルトでは、Vue の SPA のように、Vitedge ではクライアントそしてサーバー向けに 1 つのエントリポイントを持つ代わりに 1 つのエントリポイントを持ちます。これにより SSR を簡単に使い始めることができます。ただ、必要に応じて 2 つの独立したエントリポイントを使用することもサポートされています。
それでは、API からデータを取得するページコンポーネントの作成方法について説明します。
Vitedge では、フロントエンド向けにsource
フォルダの隣にあるfunctions
というフォルダに API を配置します。
ここでは、ブログ記事を表示するためのシンプルなページコンポーネントを用意し、"post"というルートに"/posts/:slug"というパスで配置しています。
props からブログポストデータを取得するために、ページコンポーネントを期待しているのが分かります。そして、それからvueuse/head
を使っていくつかの meta タグを書くためにそのデータを使い、DOM にいくつかのコンテンツを表示しています。
さて、この Post オブジェクトをどのようにしてページコンポーネントに提供するのでしょうか?覚えていると思いますが、このページのルートは post
と呼ばれています。"functions/props/"ディレクトリ配下に、ルートと同じ名前の新しいファイル "functions/props/post.ts"(TS または JS)を作成する必要があります。この関数は、ブラウザからこのルートにアクセスするたびに呼び出され、その結果を props としてページコンポーネントに渡します。ルートは"slug"パラメータを期待しており、これは実際に props 関数の引数で提供されていることがわかります。
ここでは、この"slug"パラメータを使って、データベースやコンテンツマネジメントシステムからfetch
で投稿情報を取得し、その結果を返すことができます。
とてもシンプルですね。この関数コードは Web アプリケーションにバンドルされていないので、API キーなどのプライベートな情報をここに持つことができます。また、ここではキャッシュ値を指定していることに注目してください。これは、このページコンポーネントのレンダリングされた HTML が、この秒数の間、エッジにキャッシュされることを意味します。この記事の CMS や DB のデータが変更されたことに気づいたら、Cloudflare のキャッシュにリクエストしてこの情報を削除し、次のリクエストで新しいデータを使用できるようにします。
ここには、.env
や sitemap といったファイルがあることに注目してください。
.env
ファイルには、API で利用可能な環境変数が格納されています。一方、sitemap ファイルは、この関数(props ハンドラ)のようなものですが、ブラウザで/sitemap.xml
や/sitemap.txt
にアクセスしたときに呼び出されるので、DB や CMS のデータに依存した動的なサイトマップを返すことができます。もちろん、キャッシュにも対応しています。サイトマップの代わりに、robots
ファイルや、GraphQL サーバーをセットアップするgraphql
ファイルを用意することもできます。
今日の発表は以上になります。Vitedge についてもっと知りたい方は、https://vitedge.js.org をご覧ください。