プリレンダリングを用いてVue.jsのSPAをビルドする導入から設定まで
- 公開:
- 更新:
- カテゴリ: JavaScript Vue.js
- タグ: Vue,VueCLI,VueRouter,SPA,Prerendering,SSR,PrerenderSPAPlugin,VueHead,SEO
プリレンダリングを用いて、Vue.js+Vue Routerで構築したSPAを事前描画した状態でビルドします。
開発環境
今回の開発環境は以下の通りです。
- 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で小規模なアプリケーションを作成する場合や、サーバーサイドまで手を付けるのにはちょっと自信や時間が無い場合にはとても有効なので是非試してみてください。