で、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は使い勝手がいい。

SCOUTERではエンジニアを募集しております!