+
+[Vue.js連載](/articles/20251016a/)です。ライトめなネタです。
+
+Next.jsやNuxt.jsなどのサーバーサイドレンダリング必須なフレームワークであれば、Node.jsと一緒にコンテナ化するか、Vercelなどにデプロイする方法があります。こちらはJavaScriptのウェブアプリケーションなので実行環境を用意する必要があります。
+
+一方、SPAとして作成したVue.jsなど、現代のフレームワークで作成したフロントエンドは、ビルドすると静的HTMLとJavaScriptコードになります。ただし、ファイルが存在指定なパスへのリクエストがあった場合にindex.htmlの内容をフォールバックとして返す必要があります。
+
+[Webフロントエンド設計ガイドラインのSPAのホスティング](https://future-architect.github.io/arch-guidelines/documents/forWebFrontend/web_frontend_guidelines.html#spaのホスティング)では、いくつか紹介しています。
+
+1. CloudFront+S3
+2. LB+S3
+3. LB+Webサーバー
+
+このうち、LB+S3サーバーはSPAで必要なフォールバックができないのでSPA不可となっていますが、最近、ALBでパスのリライトができるようになったので、拡張子がないパスはindex.htmlにリライトとかやれば実はいけるのでは?という気が少ししていますが、それはまたの機会に試そうと思います。
+
+* classmethodブログ: [Application Load Balancer のリスナールールでトランスフォームを構成し、ターゲットにルーティングする前にホストヘッダーや URL パスを書き換えれるようになりました](https://dev.classmethod.jp/articles/application-load-balancer-url-header-rewrite/)
+
+これ以外には、ウェブアプリケーション側に配信機能を持たせてしま鵜というのも過去に技術ブログで紹介しました。比較的高速なGoとかRustならありでしょう。
+
+* [Go 1.16のembedとchiとSingle Page Application](https://future-architect.github.io/articles/20210408/)
+
+今回はDockerイメージを作る、3番目の方法を試そうと思います。
+
+# なぜDockerにするか
+
+S3とかオブジェクトストレージにおいて配信というのがお手軽ですが、コンテナにまとめておくことでデプロイ時にまとめてフロントエンド資材を入れ替えたりがしやすいのがメリットと考えています。また、CloudFrontはインターネット公開するサービスには良いのですが、社内システムでは使えません。また、ビルド済みフロントエンドを軽量なサーバーで配信すればリソース消費は少なくて済みます。開発時もフロントエンドを触らない人にとってはありがたいのではないでしょうか
+
+せっかく作るのであればセキュリティを意識したコンテナを目指します。近年、ランサムウェアが流行っています。静的なHTML/JSでアプリを作りフロントエンドを配信するだけのコンテナにすることで攻撃面をかなり狭くできます。ですが不正なプログラムを配る踏み台にはされる可能性があるため、次の項目にもチャレンジしてみます。
+
+* シェルがないDistroless
+* フロントエンドのartifactは読み込み専用で実行ユーザーでは書き換えられない
+
+# テスト用アプリケーション作成
+
+Viteの標準的なサンプルです。
+
+```bash
+% npm create vite@latest
+Need to install the following packages:
+create-vite@8.0.2
+Ok to proceed? (y) y
+
+
+> npx
+> "create-vite"
+
+│
+◇ Project name:
+│ sample-app
+◇ Select a framework:
+│ Vue
+◇ Select a variant:
+│ TypeScript
+:
+```
+
+シングルページアプリケーションで正しく動作することをテストするために、vue-routerを入れてページをいくつか足します。
+
+```ts router/index.ts
+import { createRouter, createWebHistory } from 'vue-router'
+import Home from '../pages/Home.vue'
+import About from '../pages/About.vue'
+import Products from '../pages/Products.vue'
+import ProductDetail from '../pages/ProductDetail.vue'
+import Contact from '../pages/Contact.vue'
+import NotFound from '../pages/NotFound.vue'
+
+const routes = [
+ { path: '/', name: 'Home', component: Home },
+ { path: '/about', name: 'About', component: About },
+ { path: '/products', name: 'Products', component: Products },
+ { path: '/products/:id', name: 'ProductDetail', component: ProductDetail, props: true },
+ { path: '/contact', name: 'Contact', component: Contact },
+ { path: '/:pathMatch(.*)*', name: 'NotFound', component: NotFound },
+]
+
+const router = createRouter({
+ history: createWebHistory(),
+ routes,
+})
+
+export default router
+```
+
+ルーターを組み込みます
+
+```ts main.ts
+import { createApp } from 'vue'
+import './style.css'
+import App from './App.vue'
+import router from './router'
+
+createApp(App).use(router).mount('#app')
+```
+
+アプリケーションのトップのレイアウト側にはページナビゲーションを起きます。
+
+```html App.vue
+
+
+
+
+
+ Welcome to the SPA home page.
+This is an example About page for the SPA.
+