Nuxt.js で axios を利用しての NuxtServerError エラー対応備忘録

# Nuxt.js のSSR（ユニバーサル SSR）ではまったポイントの備忘録
自分が困ったので、また次に困った人が無駄な時間を使わないことを祈りながらの備忘録

## 実行環境
- "node": "v11.10.0"
- "npm": "6.9.0"
- "nuxt": "2.9.2"
- "@nuxtjs/axios": "5.6.0"

# NuxtServerError `Request failed with status code xxx`

`asyncData`できちんと`try`&`catch`の実装を行なっているか確認すること

```js
  // 良い例
  async asyncData({ app, error }) {
    try {
      const response = await app.$axios.$get('https://httpbin.org/status/403')
      return { hoge: response.headers.Host };
    } catch (err) {
      error({
        statusCode: err.response.status,
        message: err.response.data.message,
      });
    }
  },
```

```js
  // 悪い例
  async asyncData({ app }) {
    const response = await app.$axios.$get('https://httpbin.org/status/403')
    return { hoge: response.headers.Host };
  },
```

# RangeError `Maximum call stack size exceeded`

`catch`では`error`で返してあげること

```js
  // 良い例
  async asyncData({ app, error }) {
    try {
      const { headers } = await app.$axios.$get('https://httpbin.org/get');
      return { hoge: headers };
    } catch (err) {
      error({
        statusCode: err.response.status,
        message: err.response.data.message,
      });
    }
  },
```

```js
  // 悪い例
  async asyncData({ app }) {
    try {
      const response = await app.$axios.$get('https://httpbin.org/status/403')
      return { hoge: response.headers.Host };
    } catch (err) {
      return err.response;
    }
  },
```

# エラーページ（layouts/error.vue）のカスタマイズ
> エラーページは ページコンポーネント で、エラーが発生したときに常に表示されます（サーバーサイドレンダリング中には発生しません）。

きちんと`try`&`catch`の実装を行なっているとエラーページも必要になってくるので追記。

> デフォルトのエラーページのソースコードは、[GitHubから入手できます。](https://github.com/nuxt/nuxt.js/blob/dev/packages/vue-app/template/components/nuxt-error.vue)

公式サイトでは、[GitHubから入手できます。] と書いてあったので、そのまま使えるのかと思いましたが、

```
<% if(debug) { %>
  <p class="description" v-else><%= messages.client_error_details %></p>
<% } %>
```

上記のような記述があったため、デフォルトのエラーページだと動かなかったので、そのコンポーネントを参考に作成してみました。

## ディレクトリ構成

サンプルでしたので簡略化

```js
layouts/
--| error.vue
components/
--| nuxt-error.vue
```

## layouts/error.vue
```vue
<template>
 <div class="container">
    <nuxt-error :error='error'></nuxt-error>
  </div>
</template>

<script>
import NuxtError from '@/components/nuxt-error'

export default {
  props: ['error'],
  components: {
    NuxtError,
  },
}
</script>
```

## components/nuxt-error.vue
```vue
<template>
  <div class="__nuxt-error-page">
    <div class="error">
      <div class="title">{{ message }}</div>
      <NuxtLink class="error-link" :to="link.to">{{ link.name }}</NuxtLink>
    </div>
  </div>
</template>

<script>
export default {
  name: 'NuxtError',
  props: {
    error: {
      type: Object,
      default: null
    }
  },
  head () {
    return {
      title: this.message,
      meta: [
        {
          name: 'viewport',
          content: 'width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no'
        }
      ]
    }
  },
  computed: {
    statusCode () {
      return (this.error && this.error.statusCode) || 500;
    },
    message () {
      if (this.error.statusCode === 404) {
        return 'このページは存在しません。';
      }
      return this.error.message || `エラーが発生しました。`;
    },
    link () {
      switch (this.error.statusCode) {
        case 403:
          return { to: '/login', name: 'ログイン画面へ戻る' };
          break;
        case 404:
          return { to: '/', name: 'トップへ戻る' };
          break;
        default:
          return { to: '/', name: 'トップへ戻る' };
      }
    },
  }
}
</script>

<style>
.__nuxt-error-page {
  padding: 1rem;
  background: #F7F8FB;
  color: #47494E;
  text-align: center;
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
  font-family: sans-serif;
  font-weight: 100 !important;
  -ms-text-size-adjust: 100%;
  -webkit-text-size-adjust: 100%;
  -webkit-font-smoothing: antialiased;
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
}
.__nuxt-error-page .error {
  max-width: 100%;
}
.__nuxt-error-page .title {
  font-size: 1.5rem;
  margin-top: 15px;
  color: #47494E;
  margin-bottom: 8px;
}
.__nuxt-error-page .description {
  color: #7F828B;
  line-height: 21px;
  margin-bottom: 10px;
}
.__nuxt-error-page a {
  color: #7F828B !important;
  text-decoration: none;
}
.__nuxt-error-page .logo {
  position: fixed;
  left: 12px;
  bottom: 12px;
}
</style>
```


間違っていたら、ご指摘ください。よろしくお願いいたします。

Nuxt.js のライフサイクルをしっかり見ていれば、すんなり解決できたんだろうなと思う今日この頃。

毎度のことながら、
公式サイトはしっかり見ましょうYOという教訓を得られました。

# 参考文献
[Axios Module](https://axios.nuxtjs.org/)
[NuxtJS はじめに](https://ja.nuxtjs.org/guide/)
[Vue.js v2 スタイルガイド](https://jp.vuejs.org/v2/style-guide/)