で、NuxtのSSRって
いつ使うの?
@kotamat
誰
- kotamat
- SCOUTER CTO
- サーバー、インフラ寄り
- 🚴♂️
- Vuefesメンバー
だけkotamats
よくある会話
NuxtってSPA, SSR, generateってあるけど、どれ使うのがいいんすか?
うーん、generateかなー
とりあえず、SSRは使わなくていいんじゃね
へー、そうなんすね_φ(・_・
ソースは俺
generateはめっちゃ楽
- `nuxt generate`
- `aws s3 sync dist s3://bucket/`
- 👍
generateで初回レンダリングは早くなる
- generateするタイミングで静的なhtmlを生成するため
- 2ページ目以降はSPAモードになる
- 動的なページもgenerateしたい場合は、設定が必要
(Nuxtの)SSRは癖がある
- インフラ面
- Node.jsが動く環境が必要
- Node.jsの死活監視が必要
- コーディング面
- window, documentが参照できない
- localStorage, indexedDBが使えない
Node.jsが動く環境が必要
- EC2のインスタンスを立てる?
- firebaseのfunctionsみたいなので都度実行?
- dockernize?
- CORSどうする?
- CPUを使うので、スケール化も検討必要
あんまりフロントエンジニアとしては考えたくはない。
インフラエンジニアも考えたくはない。
Node.jsの死活監視が必要
- Nodeのプロセスが死んだら終わり
- Nodeが死んでもすぐ再起してもらう必要がある
- supervisorとか入れる?
- デプロイちょっと考える必要がある。(上げて再起動 or プロセス殺す)
あんまりフロントエンジニアとしては考えたくはない。
インフラエンジニアも考えたくはない。
window, document
UIフレームワークを使ったら
一度は通る道
localStorage, indexedDB
- 認証とかで使いたい
- process.serverのときはクッキーとか
- ダブルスタンダード発生する?
あんまりフロントエンジニアとしては考えたくはない。
インフラエンジニアは知らない。
更に…
SSR辛くね?😅
いつ使うの?😇
1. CMS系のOGP対応
OGP対応
- 調べるとまず最初に出てくるSSRの良さ
- generateでもできなくはないがビルド時に仕込む必要ある
- SSR + headless CMSなら、デプロイ後も動的にOGP対応ができる。
SCOUTERコーポレートの場合
front
server
初回アクセス
画面遷移時
generateに置き換えると
記事投稿
CI/CD等で
generate
upload
投稿のたびにgenerateが必要
他のアプローチ
Prerendering
And more...
How it works
- Prerendering対応サービスがサイトにアクセス
- 生成されたhtmlを保存
- ユーザがアクセスしたときに、そのhtmlを返却する
特に設定せずともSSRと同様の
価値を提供できる
2. 認証後のページの
表示高速化
SPAとSSR
リクエスト
SPA
DOM生成
表示
サーバ
フロントJS
SSR
サーバ
フロントJS
"フロントJSのレンダリングが遅ければ"
SPAより早い
APIからデータ取得
他のアプローチ
は今のところ無いが‥
パフォーマンスそんなに変わる?
- シンプルなDOMページにどこまでレンダリングコストかかっているのか?
- APIのデータ取得は、リージョン考えればそれほど取得時間に差は無い
- 大抵の場合は、もっと別のレイヤーの遅延のほうが深刻(クエリ戦略、画像キャッシュ等)
3. Nuxtだけでサーバーのセッション使いたい
用途(多分)
- localStorageやsessionStorageとは違う時間軸でデータを管理したい
- 認証情報をサーバーのセッションで管理したい
- 外部APIのaccess_tokenとかをブラウザに持たせない形で管理したい。
導入方法
1. 依存の追加
{
"dependencies": {
"express-session": "^1.15.6",
"nuxt-express-module": "^0.0.11",
},
}
2. ファイル構成を設定
api/
├── bodyParser.js // bodyParserの設定
├── routers // 各種APIに必要なルーティングを設定
│ └── index.js
└── session.js // session周りの設定
3. bodyParse.js
const bodyParser = require("body-parser");
module.exports = function BodyParserModule(moduleOptions) {
this.addServerMiddleware(bodyParser.json());
};
4. session.js
const session = require("express-session");
module.exports = function SessionModule(moduleOptions) {
const options = {
secret: "hogehogehoge",
resave: false,
saveUninitialized: false,
...moduleOptions
};
this.addServerMiddleware(session(options));
};
5. routes/index.js
const { Router } = require("express");
const router = Router();
// 外部APIとの認証処理などを記述
// clientId secretなどを環境変数とかで格納しておく
router.use("/api/auth", require("./auth"));
//...
module.exports = router;
6. nuxt.config.js
module.exports = {
modules: [
"~/api/bodyParser",
"~/api/session",
[
"nuxt-express-module",
{
expressPath: "api",
routesPath: "api/routers"
}
]
]
};
あとは普通に立ち上げるだけ
- `nuxt`
- `nuxt build` + `nuxt start`
他のアプローチ
Firebase Authentication
主要なSNSアカウントで
ログインできる
SNS認証周りはfirebase側で持ってくれる
- Authentication > ログイン方法のところで必要なものだけ有効化
- Twitter等で発行したclient_id, secretを登録
- 👍
firebaseのパッケージがよしなに認証
export default {
middleware: "guest",
async mounted() {
try {
// 認証後のリダイレクトから結果を取得
const result = await firebase.auth().getRedirectResult();
const user = result.user;
if (!user) {
// providerを設定し、signinする
const provider = new firebase.auth.TwitterAuthProvider();
await firebase.auth().signInWithRedirect(provider);
return;
}
// providerに紐づくaccess_token等が取得できる
const token = result.credential.accessToken;
const secret = result.credential.secret;
} catch (err) {
console.error(err);
}
}
};
4. generateでできないことをしたい
generateでの限界
- 生成されたhtmlではmiddlewareは使えない
- nuxtServerInitでの初期アクセスのハンドリングはできない
middlewareを使いたいパターン
- 会員制のサイトでの認証可否に対するハンドリング
- 表示前に行う処理が複数ページで共通の場合
nuxtServerInitを使いたいパターン
- masterデータの初回取得
- セッションデータを取得し、storeに格納する
- その他初回のみ実行したい処理
他のアプローチ
middlewareしたいところはgenerateしない
module.exports = function () {
this.nuxt.hook('generate:extendRoutes', async routes => {
const whiteList = ['/about', '/login'];
const routesToGenerate = routes.filter(page =>
whiteList.includes(page.route)
);
routes.splice(0, routes.length, ...routesToGenerate);
});
};
clientでnuxtServerInitをdispatchする
context.store.dispatch('nuxtServerInit')
pluginなどで初回ロード時に一回だけdispatchするようにすれば動く
まとめ
- 静的サイトの場合はgenerateでどうにかなる
- 会員制サイトなどは、middleware使うことが多いのでgenerateだと厳しいことがある
- SSR固有の要素は、代替技術が存在したりする
- ちゃんと設計から入ってSSRがどうしても必要であれば入れる
- とはいえSSRの実装自体はNuxtはめちゃくちゃ簡単なので、SSRしたいならNuxtは使い勝手がいい。