# Universal(ユニバーサルな)コードを書く
先に進む前に、 "Universal" コード、つまりサーバとクライアントの両方で動くコードを書くとき、その制約について考えてみましょう。ユースケースとプラットフォームの API の違いにより、異なる環境で実行したコードの動作は全く同じものにはなりません。ここでは知っておくべき重要な項目について説明します。
# サーバ上のデータリアクティビティ
クライアントだけで実行するアプリケーションでは、すべてのユーザがブラウザでアプリケーションの新しいインスタンスを使用します。サーバサイドレンダリングでも、同じ振る舞いが必要とされます: リクエスト間で状態の汚染がないように、各リクエストは新しく独立したアプリケーションのインスタンスが必要になります。
実際のレンダリング処理は決定的であることが求められるので、サーバ上でデータを "プリフェッチ" することもあります - これはレンダリングを開始するとき、アプリケーションの状態はすでに解決済みであるということです。つまり、サーバ上ではデータのリアクティビティは必要ないためデフォルトで無効化されています。データのリアクティビティを無効にすることで、データをリアクティブなオブジェクトに変換する際のパフォーマンスコストを無視できます。
# コンポーネントのライフサイクルフック
動的な更新がないため、 SSR 中に呼び出される ライフサイクルフック は beforeCreate
と created
だけになります。つまり、 beforeMount
や mounted
など他のライフサイクルフックにあるコードは、クライアントでのみ実行されるということです。
もうひとつの注意点は、 beforeCreate
と created
でグローバルな副作用を引き起こすコードは避けるべきということです。例えば setInterval
を使ったタイマーの設定です。クライアントサイドのみのコードでは、タイマーを設定して、 beforeUnmount
や unmounted
でそれを破棄します。しかし、 SSR 中には destroy フックが呼び出されないために、タイマーは永遠に残り続けることになります。これを避けるため、代わりに副作用を引き起こすコードを beforeMount
や mounted
に移動してください。
# プラットフォーム固有の API へのアクセス
Universal コードは、プラットフォーム固有の API アクセスを前提にできないため、コードが window
や document
のようなブラウザ専用のグローバル変数を直接使用している場合、 Node.js で実行するとエラーが発生しますし、その逆も同様です。
サーバとクライアントで共有されたタスクで、異なるプラットフォームの API を使っている場合は、プラットフォーム固有の実装を Universal API の中にラップするか、これを行うライブラリの使用をお勧めします。例えば、 axios (opens new window) は、サーバとクライアントの両方に同じ API を提供する HTTP クライアントです。
ブラウザ専用の API の場合は、クライアント用のライフサイクルフックの中で遅延アクセスするのが一般的なアプローチです。
サードパーティのライブラリが Universal での利用を考慮していない場合は、サーバサイドレンダリングしたアプリケーションと統合するのは難しいかもしれないことに注意してください。グローバル変数の一部をモックして動かすことは できるかも しれませんが、それはハック的なもので、他のライブラリの環境検出するコードと干渉する可能性があります。
# カスタムディレクティブ
ほとんどの カスタムディレクティブ は DOM を直接操作するため、SSR 中にエラーが発生します。このため、抽象化の仕組みとしてはディレクティブではなくコンポーネントを使うことをお勧めします。