NuxtのSSRモードで一部だけSSRをスキップする方法

そういえばわざわざ探して公式ドキュメントに追加しておきながら日本語の情報としてまとめてなかったなぁと思いメモ。

## Nuxt の SSR モードにおける悩み

Nuxt はデフォルトでサーバーが立ち上がって Node.js サーバーがサーバーサイドレンダリングを行う SSR モードで起動するようになっている。

SSR をしたい場合はどうしても SSR モードを使う必要があるけれど、とはいえ例えば SSR 非対応な Vue コンポーネントを読み込んでいる時に、 plugins で ssr: false にしていてもカスタムコンポーネントを記述していると問題となったりしてすこし具合が悪い。

そんな時の、「ここだけSSRしないでおきたい」という要望も、 Nuxt は対応している。

## `<no-ssr>` コンポーネント

Nuxt には、本体にデフォルトで登録されたカスタムコンポーネントである `<no-ssr>` コンポーネントが存在する。

これは内部的には単一の slot を受け取った上で、 `mounted` フックの後にのみコンポーネントがレンダリングされる仕様となっており、 mounted フックは SSR 時は実行されない( componentDidMount である)ため、 SSR 時はスキップされた上で、ブラウザ上では内部に書かれているものが全て動く。という仕様になっている。

これを使うことで、例えば既存プロジェクトに Nuxt を入れた場合や、 UI 系のフレームワークやライブラリが jQuery 依存である場合など、 SSR が何らかの理由で不可な場面でも、段階的に SSR を導入することができる。

### TIPS: ベースは `egoist/vue-no-ssr`

実はこの `<no-ssr>` コンポーネント、 Nuxt の内部に存在するが、作りとしては Vue コアチームの EGOIST 氏が作成した vue-no-ssr というライブラリのソースコードのコピペでできている。

その為、 Nuxt でない Vue の SSR においては、同じ使い勝手で実装できる egoist/vue-no-ssr を利用すると良い。

https://github.com/egoist/vue-no-ssr

## 使いかた

以下に基本的な使いかたをご紹介する。

### 基本的な利用

まずは一番基本的なものから。

no-ssr はグローバルなコンポーネントとして定義されているため、別途読み込む必要はなく、そのままこのように SSR したくない場所をラップしてやると良い。

```vue:index.vue
<template>
  <div>
    <h1>Hello</h1>
    <no-ssr>
      <h2>World</h2>
    </no-ssr>
  </div>
</template>
```

実際の表示はこのようになっており、ブラウザでは正しく表示されているが、 SSR がスキップされていることがわかる。

![Screen Shot 2018-03-17 at 12.24.04.png](https://qiita-image-store.s3.amazonaws.com/0/75633/bcfcd541-818c-00ab-043b-0770482069a0.png)


![Screen Shot 2018-03-17 at 12.23.49.png](https://qiita-image-store.s3.amazonaws.com/0/75633/62a4e58b-b1d5-9ef7-ac3f-84a027eaae79.png)

```html
<body data-n-head="">
    <div data-server-rendered="true" id="__nuxt"><div class="nuxt-progress" style="width:0%;height:2px;background-color:#3B8070;opacity:0;"></div><div id="__layout"><div><div><h1>Hello</h1><div class="no-ssr-placeholder"></div></div></div></div></div><script type="text/javascript">window.__NUXT__={"layout":"default","data":[{}],"error":null,"serverRendered":true};</script><script src="/_nuxt/manifest.js" defer></script><script src="/_nuxt/vendor.js" defer></script><script src="/_nuxt/app.js" defer></script>
  </body>
```

### プレースホルダの挿入

今ので表示に関しては何も問題がない。
とはいえ、 no-ssr のところに何も表示されないのはそれはそれで困るというシチュエーションもそれなりにある。
そういった場合は、 placeholder プロパティを設定してやることで、プレースホルダ（仮文字）として、任意の文字列を挿入することができる。

例えばわかりやすくするため、こうしてみる。

```vue:index.vue
<template>
  <div>
    <h1>Hello</h1>
    <no-ssr placeholder="SSR">
      <h2>Browser</h2>
    </no-ssr>
  </div>
</template>
```

実際の表示は以下のようになり、 SSR 時とブラウザで出し分けができていることがわかる。

![Screen Shot 2018-03-17 at 12.26.58.png](https://qiita-image-store.s3.amazonaws.com/0/75633/1ebc6c50-b505-79f3-9cc3-f56bd2d1fe07.png)

![Screen Shot 2018-03-17 at 12.26.50.png](https://qiita-image-store.s3.amazonaws.com/0/75633/93a96ee7-cbbc-464f-b474-a9804ce84d09.png)

## 終わりに

`<no-ssr>` はドキュメントを先日追加しただけで、実装自体は昨年の夏の時点で行われている。
そのため、まだ正式版を使っていない(alpha や rc を利用している)プロジェクトの場合でも利用可能なので、 Nuxt を利用している場合は、古いバージョンだからといってわざわざ vue-no-ssr をインストールする必要はない。

SSR 非対応ライブラリやコードベースに悩まされてやむなく `mode: 'spa'` を利用している場合は、試しに `<no-ssr>` を使ってみると良いかもしれない。

## Links

- API: The &lt;no-ssr&gt; Component - Nuxt.js
    - https://nuxtjs.org/api/components-no-ssr#the-lt-no-ssr-gt-component
- egoist/vue-no-ssr: Vue component to wrap non SSR friendly components (428 bytes)
    - https://github.com/egoist/vue-no-ssr
- no-ssr.js
    - https://github.com/nuxt/nuxt.js/blob/master/lib/app/components/no-ssr.js