仮想DOM
nuxt.js の話をする前に、 vue.js の話をするさらに前に、仮想DOM
の話をする必要があります。
Web ページをレンダリングするにあたって、近年の流行は 仮想DOM
を用いたビューライブラリです。
一世を風靡した jQuery では、下記のように DOM 要素を直接書き換えていました。
$('#some-id').addClass('some-class')
// ≒ document.getElementById('some-id').className += ' some-class'
実際に画面に表示されている(=開発者ツールの Elements タブに表示されているもの) DOM 要素を更新する場合、画面の座標などの再計算が走り、描画しなおす特性があるため、多くのコストがかかります。
そのような問題を解消するために 仮想DOM
という概念が導入されました。
これは JavaScript 上に DOM 要素と対になるツリー型オブジェクトを用意し、データの更新時に「本当に描画しなおす必要があった時だけ」描画しなおすというものです(雑な説明)。
また、 JavaScript 上に data.authenticated = false
という値があった時、 data.authenticated = true
と値が変更された時に、同時に DOM 要素を更新する(if/for 等)ことも可能です。つまり「データと DOM 要素の連携 => リアクティブ」という機能です。
const response = await (await fetch('/auth', data)).json()
if (response.authenticated) {
$('.authenticated').show()
} else {
$('.authenticated').hide()
}
このような JavaScript 上の処理が、下記のようになります。
const response = await (await fetch('/auth', data)).json()
this.authenticated = response.authenticated
どちらがわかりやすいか、一目瞭然ではないでしょうか。
これを実現するため、 Vue.js では <template>
タグを使って事前に DOM 要素の if
分岐や for
ループをテンプレートとして定義します。
React でも render
関数にテンプレート DOM 文字列を渡すので、同じ手法が用いられていることがわかります。
Vue.js のチュートリアル動画 が非常に単純で理解しやすいと思います。 PHP エンジニア目線で言えば、 Smarty で HTML をテンプレート描画する代わりに、 JavaScript 内でリアルタイムに描画を変更する形です。
Vue.js
例えば React は jsx
というファイル形式で JavaScript の中にそのままタグを埋め込んだりするので、若干違和感がありますが、 Vue.js は非常にとっつきやすい(導入障壁が低い)です。
- 最初はコンパイルなしで
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
を埋め込むだけで動くようになる - ライブラリとしての機能(API)は最小限で、追加で覚える要素が数えるほどしかない
また、周辺のエコシステムも非常に活発かつ大規模で、 JavaScript によくある「数年ですぐ廃れるライブラリ」とは一線を画していると思います。
これは仮想 DOM という概念自体が廃れる可能性が低いためです。
本編: nuxt.js って何ですか?
Vue.js 単体は、ただ描画を手助けしてくれるだけのビューライブラリです。要するに JavaScript 版 Smarty といっても大体合っています。
しかし、実際のアプリケーションの立場になってみると、それだけでは不足している場合があります。
- 複数のページをひとまとめにした シングルページアプリケーション(SPA) を構築しづらい
- サーバサイドレンダリング(SSR)ではないので、クローラ(例: Twitter の URL 展開)が中身を見れない可能性がある
- 同じページを長時間操作する場合、状態の管理がしづらい(グローバル変数に依存してしまうなど)
それらの「実際の現場」で不足しがちな周辺システムをまるっと含めて、一つのアプリケーションとして提供可能な状態にしてくれるフレームワークが nuxt.js
です。
vue-router によるルーティング
昨今、 1 ページ 1 HTTP GET リクエストをして HTML を全て取得してくるという作業は不要になりつつあります。
SPA に代表されるように、 HTML ファイルの取得は最初の一回だけにとどめ、残りのルーティングは全てクライアント側で処理することが可能となっています。
これは GET リクエストにより毎回 html/js/css などのリソースに対し TCP 接続を行うコストを減少させ、画面の更新速度を大幅に高速化することが出来ます。軽いページに関してはほぼ一瞬で遷移が可能です。
例えば nuxt.js のページ自体 が vue-router(in nuxt.js) で構成されていますが、ページの遷移がかなり早いと思いませんか?
開発者ツールで Network を見ると、 ajax リクエストと関連リソース(png など)のリクエストしかありません。
これはサーバ負荷も軽減されるため、嬉しいことだらけですね。
Vuex による状態管理
Vuex は、 Vue.js 上の状態(=変数)の管理を安全に行うためのライブラリです。
実際に変数の変更を行う関数を1箇所だけにして、必ず 1 つのルートからしか状態を更新出来ないような仕組みを提供します。
そうすることで、変数をいつ誰が変更したかのトランザクション(履歴)を確実に追うことが出来ます。
Vue 用の開発者ツール を使えば、履歴をいったり来たりすることも可能です。デバッグがはかどりますね。
Vuex はその説明ページにも記載されていますが、「大規模な状態管理が必要な SPA アプリケーションなどで使うための少し複雑なシステム」なので、ちょっとした状態を保持するだけのために利用する必要はありません。
SSR
nuxt.js はそれ自体が node.js サーバとしてプロセスを提供します。
そうすることで、この node.js サーバに直接アクセスした時は Vue Server Renderer を用いてサーバ側で Vue.js をレンダリングし、クローラが正しく内容を認識出来るようになります。
また、 JavaScript の処理が遅いクライアントでも、サーバ側でレンダリングを行うため、初回表示を素早く行うことが出来るようになります。
トレードオフとして、 node.js サーバが必要な点、「サーバでレンダリングされる場合」と「ブラウザでレンダリングされる場合」の二種類の状態をうまくハンドリングする必要がある点があります。
静的なページ(レンダリングに外部リソースの読み込みを不要とする)の場合は、うまくキャッシュを効かせることでサーバ負荷を軽減させることが可能です。
nuxt.js ではどちらでレンダリングされているかをできるだけ意識しなくてすむように配慮されています。
静的ファイル生成
動的なページ(/watch/sm9
など)ルーティングには対応しきれませんが、全てが静的なページで構成される場合は、 node.js サーバなしで完全に CDN に載せられるファイルを生成出来ます。
これにはクローラが取得する <meta>
タグのなどが事前生成されるため、 SEO 的な問題も発生せず、 node.js サーバの管理コストもなくなるため、非常に心強い機能です。
npm run generate
コマンド一つで、 ./dist
ディレクトリの中に完全な静的ファイルが生成されます。後はこのファイルを S3 などにアップロードして CDN に任せるだけです。超簡単。
ちなみに SPA モードで生成した場合、 404 ルートを /index.html
にリダイレクトさせる設定を行うことで動的なページルーティングも可能ですが、 404 と返ってくるけども実際は存在するページに対してクローラがアクセスした場合 <meta>
タグが /index.html
のものとなってしまうため、 SEO 的にアウトでした(実験結果)。
Progressive Web Apps
最近のブラウザは「そのページがまるで 1 つのネイティブアプリであるかのように動作させる」機能がどんどん実装されています。
- 「ホーム画面に追加」を促すポップアップ
- ホーム画面から直接アクセスした場合、ブラウザのメニューバーなどを非表示にすることなどが可能になります
- ローカルプッシュ通知
- WebWorker を使って、ネイティブアプリでなくとも端末にプッシュ通知を送れます
- オフライン処理
- ネットワークが繋がらない状態でも、キャッシュされたデータを利用したりと適切にハンドリング出来ます
これらを実現するのが PWA と総称されていて、 nuxt.js ではコミュニティプラグインとして PWA サポートが提供されています。
ビルドの隠蔽
今フロントエンドで面倒事となっている webpack
などのビルド構成を意識する必要はありません。自動的に最適な形にビルドしてくれます。もう webpack.config.js
を頑張って構築しなくていいんです。
ここまでをまとめると、
- Vue ファイルで記述できること
- コードを自動的に分割すること
- サーバーサイドレンダリング
- 非同期データをハンドリングするパワフルなルーティング
- 静的ファイルの配信
- ES6/ES7 のトランスパイレーション
- JS と CSS のバンドル及びミニファイ
- Head 要素の管理
- 開発モードにおけるホットリローディング
- SASS, LESS, Stylus などのプリプロセッサのサポート
- HTTP/2 push headers ready
- モジュール構造で拡張できること
と、多種多様でプロダクションレディな Vue.js ベース環境を一発で構築出来るのが nuxt.js です。
ちなみに React ベース環境は next.js があります。これの後発FWとなっています。 React ユーザはこちらが選択肢に挙がるでしょう。
API サーバ(PHP など)を完全に json のやり取りだけにとどめ、フロントエンドを nuxt.js に完全に分離して分業することも可能です。
PHP エンジニアからすれば Unity クライアントと通信するのと同じ感覚で、 JavaScript / デザイナーからすると自分たちだけで(PHP を意識することなく)開発を進められるというメリットが、開発段階においては非常に大きいですね。