プリレンダリングを用いてVue.jsのSPAをビルドする導入から設定まで
- 公開日
- 更新日
- カテゴリ:Vue.js
- タグ:Vue

プリレンダリングを用いて、Vue.js + Vue Router で構築した SPA を事前描画した状態でビルドします。
Contents
開発環境
今回の開発環境は以下の通りです。
- Vue.js 2.x
- Vue CLI 3.x
- Vue Router 3.x
尚、今回は Vue CLI を用いて Vue Router の環境を前提で進めます。環境構築については、
VueRouter の基本とルーティングを構築するはじめの一歩
を確認してください。
プリレンダリング
プリレンダリング(Prerendering)は、事前描画を意味します。
Vue.js アプリケーションを構築し公開する時にはビルドを行い、HTML/CSS/JS ファイルなどを書き出しますが、 その際に、複数の機能(画面)も予め書き出してしまおう。というものです。
どこで使えるのか
例えば、Vue.js を使って SPA を構築する際に Vue Router を利用して複数の機能(画面)を用意するとします。
ただし元々1つのファイルしか物理的に存在しないのがベーシックな SPA のため、Google のインデックスが意図通りに行かず SEO 的に不利になる場合があります。
そんな時に強い味方となるのが SSR(サーバーサイドレンダリング)ですが、サーバ側処理の構築が必要です。
また、VueRouter の基本とルーティングを構築するはじめの一歩 で紹介したルーティングで工夫をする場合でも、catch-all フォールバックを用いる場合にサーバ側の設定が必要です。
ただしそもそも、あなたや私の目の前にあるそのアプリケーションには、SSR などサーバ側連携を導入し運用するコストに見合ったアプリケーションの規模でしょうか? そこをまず考える必要があります。なぜならそれには、時間も労力もかかるからです。
小規模なアプリケーションであってもコストを最小に抑え、確実な描画結果を担保したい。
そこで登場するのが、プリレンダリングです。
どういう仕組みなのか
複数の画面があるということは、ルーティングを行っているということになりますが、ルート以外にもパスが存在しているということになります。例えば
- /
- /about
- /product
- /recruit
みたいな感じです。
Vue CLI で通常のビルドを行うと、生成される HTML ファイルは1つだけ(index.html)です。
これをプリレンダリングを用いることで、以下の HTML ファイル(とその関連ファイル群)を予め生成することができます。
- /index.html
- /about/index.html
- /product/index.html
- /recruit/index.html
完全に静的なページとしてリリースする事で、SSR などを用いなくても確実な画面分割を実現できます。
これが、プリレンダリングです。
プリレンダリングの導入と設定
それでは導入と設定を行っていきます。
まずは必要となる Prerender SPA Plugin を導入します。
https://github.com/chrisvfritz/prerender-spa-plugin
以下の npm コマンドを叩いてインストールします。
# Prerender SPA Plugin インストール
npm install prerender-spa-plugin
インストールが完了したら、プロジェクトルートに設定ファイルを作成し、Webpack の設定を行います。
- vue.config.js
-
const path = require('path') const PrerenderSPAPlugin = require('prerender-spa-plugin') module.exports = { configureWebpack: () => { return { plugins: [ new PrerenderSPAPlugin({ staticDir: path.join(__dirname, 'dist'), routes: [ '/', '/about', '/product', '/recruit' ] }) ] } } }
- staticDir プロパティに出力先を指定しています。
- routes プロパティにプリレンダリングにて生成する画面を指定しています。
これであとは Vue CLI でビルドを行う事で、プリレンダリングが行われます。
# ビルド実行
npm run build
プリレンダリングを行わない通常のビルドと、プリレンダリングを行った場合のビルドの違いを見てみます。
# 通常のビルド
statics/not-prerendering
├── index.html
├── favicon.ico
├── css
│ └── app.7e7cfee3.css
└── js
├── app.840c5d47.js
├── app.840c5d47.js.map
├── chunk-vendors.183348fa.js
└── chunk-vendors.183348fa.js.map
# プリレンダリングを用いたビルド
statics/prerendering
├── index.html
├── favicon.ico
├── about
│ └── index.html
├── product
│ └── index.html
├── recruit
│ └── index.html
├── css
│ └── app.7e7cfee3.css
└── js
├── app.43d1fddd.js
├── app.43d1fddd.js.map
├── chunk-vendors.183348fa.js
└── chunk-vendors.183348fa.js.map
通常のビルドはルートにのみ index.html が生成され、スクリプトによって画面が切り替わる動作になりますが、 プリレンダリングを用いたビルドの場合はこのように、各画面ごとに index.html が生成されるので、アクセス自体は 静的なページの遷移と同じになります。 つまり meta や OGP などの head 情報は予め切り替えられた状態で書き出される。ということになります。
サンプルページ
通常のビルド
プリレンダリングを用いたビルド
サンプルページで確認すると、ルーティング周りの挙動の違いも確認できます。
通常のビルドの場合であれば、catch-all フォールバックなどを行わないとルート URL 以外で画面をリロードした場合に 404 エラーが発生しますが、 プリレンダリングを行った場合は物理的にページが存在するので画面をどこでリロードしても正常に表示されます。
任意のパスでのビルド&プリレンダリング
通常のビルドはドメインルートを起点として行われますが、 デプロイ先の環境によっては出力先のパス(ディレクトリ階層)が異なっていたりする場合に、それ用にビルドしたい時があります。 例えば以下のような場合です。
開発 https://xxx.com/index.html 本番 https://xxx.com/parent/child/index.html 通常のビルドの場合はドメインルート起点なので開発時の URL で動作するようにビルドされますが、 これを本番の URL で動作するようにビルド時にまるっとパスを変更する事も出来ます。 それには、以下の用に設定します。
- vue.config.js
-
const path = require('path') const PrerenderSPAPlugin = require('prerender-spa-plugin') module.exports = { // [追加] 付与したいパスを指定 publicPath: '/parent/child/', // [追加] jsやcssなどベースセットの出力先を指定 outputDir: path.join(__dirname, 'dist/parent/child'), configureWebpack: () => { return { plugins: [ new PrerenderSPAPlugin({ staticDir: path.join(__dirname, 'dist'), // [追加] プリレンダリングを行ったファイルの出力先を指定 outputDir: path.join(__dirname, 'dist/parent/child'), // [追加] 起点となるindex.htmlのパスを指定 indexPath: path.join(__dirname, "dist/parent/child/index.html"), routes: [ '/', '/about', '/product', '/recruit' ] }) ] } } }
こうする事で、meta タグの各種ファイル読み込みからナビゲーションメニューのパスまで全てが、指定したパスが付与された状態でビルドされます。
コンパイルしてからプリレンダが走るので、ベースセットも含め適切な場所に出力してあげる事がポイントです。
<!-- before -->
<link rel="icon" href="/favicon.ico">
<link href="/js/app.51d47241.js" rel="preload" as="script">
<div id="nav">
<a href="/">Home</a> |
<a href="/about">About</a> |
<a href="/product">Product</a> |
<a href="/recruit">Recruit</a>
</div>
<!-- after -->
<link rel="icon" href="/parent/child/favicon.ico">
<link href="/parent/child/js/app.51d47241.js" rel="preload" as="script">
<div id="nav">
<a href="/parent/child/">Home</a> |
<a href="/parent/child/about">About</a> |
<a href="/parent/child/product">Product</a> |
<a href="/parent/child/recruit">Recruit</a>
</div>
出力ディレクトリも指定の通りの構成になります。
dist
└── parent
└── child
├── favicon.ico
├── css
│ └── app.7e7cfee3.css
├── js
│ ├── app.51d47241.js
│ ├── app.51d47241.js.map
│ ├── chunk-vendors.183348fa.js
│ └── chunk-vendors.183348fa.js.map
├── index.html
├── about
│ └── index.html
├── product
│ └── index.html
└── recruit
└── index.html
まとめ
以上で作業は終了です。 Vue.js で小規模なアプリケーションを作成する場合や、サーバーサイドまで手を付けるのにはちょっと自信や時間が無い場合にはとても有効なので是非試してみてください。