ポートフォリオ作ったらNuxtJSを理解できたのでやったことを書いてく


今年で30になったプログラマですが、ポートフォリオサイトを作りました。

<img width="999" alt="スクリーンショット 2020-01-15 23.05.24.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496802/c3ff6915-3d18-e60b-3641-fec3fb8e999b.png">


ポートフォリオサイトは[こちら](https://festive-yonath-a04e1e.netlify.com/)
Storybookは[こちら](https://quirky-hoover-d5f9da.netlify.com/)

# はじめに
Vue、NuxtJSともに未経験だったので一通り[チュートリアル](https://ja.nuxtjs.org/guide/)をやったのですが、正直全然理解出来てない感があって「これじゃあ戦力にならんなぁ」と思い、ある程度のサイト作りぐらいは出来るようにするためポートフォリオサイトを作成しました。実際にやってみて、約１ヶ月で困らない程度には理解できたかなと思っています。この程度のポートフォリオサイトであればベライチのhtmlで済む話なのですが、昨今のフロントエンドは状態管理、SSR×SPAなどいろいろやらなければいけないことが多いので基本的な技術は全て使うよう開発をしました。

# 構成
フロントがNuxtJS、Vue、ホスティングがNetlify、バックエンドが.NET Core、C#をコンテナ化し、ホスティングはHerokuとなっています。
![system.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496802/b2632841-97ed-178a-2c98-c6d6534b47dc.jpeg)
バックエンドは今回の勉強対象外なので、経験のある（なにも調べなくても書ける）.NET Coreを使いました。公開しないのであればJSON Serverでも良いかもしれません。

# 期間
<img width="239" alt="スクリーンショット 2020-01-15 10.51.52.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496802/c9f793d2-7ca0-ddc2-f649-e419c1168503.png">
昨年の12/22から開始したので約１ヶ月間、１日１時間程度コツコツと進めました。僕は毎日集中出来るタイプでもなくたまにサボりたくなるので、そんな気分のときはスタイルいじったり、調べ物する日にしたりと気分転換してました。

# 決め事
最近のフロントエンドはやることが多いです。やることは多いけれども、３大フレームワークで抑えておくべきスキルセットは共通してきたと思うので以下にあげる技術は絶対に取り入れると決めました。

- HttpClient(axios)
- SSR(universal)
- 状態管理(vuex)
- eslint + prettier
- jest
- storybook

jestはカバレッジを100%、storybookはknob addonを使った動的なコンポーネントの変更を出来るようにしています。あとこれは僕の完全な感覚なのですが、vueはtypescriptの導入が一歩遅く導入しているところは多くはないと思うのであえてesで書くようにしました。

# 雰囲気を掴むためにチュートリアルを一周する
NuxtJSとはなんぞや？というところからVuexまで一通りやります。１日３０分、１週間やれば一通り出来ると思います。僕はリポジトリを作ってチュートリアルのやりながらコミットを積んで行きました。
<img width="434" alt="スクリーンショット 2020-01-15 22.29.08.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496802/f559c4b4-f431-fea8-75a4-9999652be484.png">
[勉強用リポジトリ](https://github.com/ikuosaito1989/nuxtjs-input)


# カバレッジを100％にする
はじめにカバレッジを100%にしましょう。テストははじめにやらないと一生出来ないので`create-nuxt-app`の直後にやりましょう。

```json:jest.config.js
coverageThreshold: {
  global: {
    branches: 100,
    functions: 100,
    lines: 100,
    statements: 100
  }
},
```
これでもう逃げられません。さらに初期設定では`page`と`component`しかテスト対象でないのでstoreやその他もろもろ追加しましょう。

```json:jest.config.js
collectCoverageFrom: [
  '<rootDir>/src/**/*.{js,vue}',
  '!<rootDir>/src/layouts/**/*',
  '!<rootDir>/src/plugins/**/*'
],
```
# Storybookを準備する
これもはじめにやらないとやる気がなくなるので早いうちに準備しておきましょう。正直、初学でテストとStorybookは鬼畜です。非常に辛いのですが、簡単な設計にするようになり、全体的に可読性が自然とよくなるのでここは我慢です。テスト出来るってことはある程度構造化されているってことなんですねー。

Storybookは自動セットアップがあるので私は`npx -p @storybook/cli sb init --type vue`しました。後々`addons`や`preview-head`など必要になるので手動セットアップの方がよいかと思いました（僕はここで時間を潰しました）。

[storybookガイド](https://storybook.js.org/docs/guides/guide-vue/)

componentを動的に変更するためにknobを入れましょう
`npm i -save @storybook/addon-knobs`

```js:.storybook/addons.js
import '@storybook/addon-knobs/register'; 
```

僕が思うstorybookの位置付け的にデザイナーさんとのやりとりツールというイメージだったのでアドオンはknobsしか入れませんでした。

これはどちらでもよいかと思いますが`npm run build-storybook`すると`storybook-static/`がソース管理対象になるので.gitignoreに追加してもよいかもしれません。

# http-serverを入れる
今回はuniversalモードで作るのでNetlifyに`nuxt generate`でホスティングしました。ローカルで確認するためにhttp-serverを入れます。

`npm i http-server --save`

```json:package.json
"scripts": {
  ...
  "webserver": "http-server ./dist -o -p 8888"
},
```

これでdist直下のファイルを確認することが出来ました。

# 拡張を入れる
開発するための必需品です。私はVSCodeを使っているのでVSCodeのプラグインを記載します。

- Vue.js devtools

Storeの中身など見れるので便利です。必須。
https://chrome.google.com/webstore/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd

- VSCode拡張

ここを参考にさせてもらいました。
https://qiita.com/dayoshix/items/c61a75a971331418c348

# Netlifyの準備をする

- appのパスを変える

`npx create-nuxt-app <project-name>`をすると`<project-name>`直下にnuxt appが生成されるのでNetlifyでデプロイエラーになりました。`nuxt.config`や`package.json`はrootに持っていき、ソースは別のディレクトリで管理するよう変更しました。

```js:nuxt.config.js
export default {
  ...
  srcDir: 'src/'
  ...
}
```

- サーバーサイドレンダリングの対策
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496802/fd23e2d3-a078-14b6-e62a-3bb11b8498a4.png)

`nuxt generate`ではindex.htmlしか生成されないため、リロードすると「Page Not Found」になってしまいます。こちらの対策方法は以下の通りです。

```js:nuxt.config.js
export default {
  ...
  generate: {
    fallback: true
  }
  ...
}
```
[Netlify へデプロイするには？](https://ja.nuxtjs.org/faq/netlify-deployment/)

# エラーハンドリングを作る

- エラーページ

存在しないページには404ページへ、エラーは500ページへなどエラーハンドリングも実装します。nuxtはエラーページという機能を持っているので以下のように実装します。詳しくは[こちら](https://ja.nuxtjs.org/guide/views/#%E3%82%A8%E3%83%A9%E3%83%BC%E3%83%9A%E3%83%BC%E3%82%B8)に載っています。

```vue:layouts/error.vue
<template>
 <div class="container">
    <h1 v-if="error.statusCode === 404">ページが見つかりません</h1>
    <h1 v-else>エラーが発生しました</h1>
    <nuxt-link to="/">ホーム</nuxt-link>
  </div>
</template>
<script>
export default {
  props: {
    error: {
      type: Object,
      default: null
    }
  }
}
</script>
```
`error.statusCode`でh1を切り替えています。リッチナブルにしたい場合はステータスコード毎にcomponentを作ってもいいかもしれませんね。

- axiosのエラーハンドリング

axiosでエラーが発生した場合はinterceptorsのonErrorヘルパでキャッチしました。これでエラーページへ遷移します。

```js:plugins/axios.js
export default function({ $axios, error }) {
  // ref. https://axios.nuxtjs.org/helpers
  $axios.onError((err) => {
    const code = parseInt(err.response && err.response.status)
    error({ statusCode: code })
  })
}

```

# ライブラリを使う
やっとちゃんとしたコーディングに入れるようになってきました。

- VeeValidate

フォームのバリデーションがすごく簡単に書けるライブラリです。問い合わせフォームに使いました。
<img width="773" alt="スクリーンショット 2020-01-15 0.32.17.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496802/11ba4ca1-6da1-37ab-4cab-10636e54f025.png">

```js:plugins/vee-validate.js
import { extend } from 'vee-validate'
import { required, email } from 'vee-validate/dist/rules'

// No message specified.
extend('email', email)

// Override the default message.
extend('required', {
  ...required,
  message: 'This field is required'
})
```
上記のファイル作成して実行すると
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496802/632fc9a3-100e-64c2-831d-c9d0f05fd07d.png)

Babelで変換しないといけないので以下を追加します。

```js:nuxt.config.js
build: {
    transpile: ['vee-validate/dist/rules'],

    extend(config, ctx) {}
    ...
}
```
[VeeValidate公式](https://logaretm.github.io/vee-validate/)

- axios-mock-adapter

axiosのモックを作成するためのライブラリです。テストするためには必須でした。

[axios-mock-adapter公式](https://github.com/ctimmerm/axios-mock-adapter)

- marked

Nuxtを勉強しているはずが、cssにかける時間が多かったのでマークダウンを投入しました。ポートフォリオの詳細ページで利用しています。
<img width="822" alt="スクリーンショット 2020-01-15 0.41.10.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496802/459b9337-480a-5583-78b2-f72ee2a3011a.png">

- analytics-module

公式が出しているanalyticsのライブラリです。ものすごく楽です。

# 妥協する
カバレッジは100%にすると書きましたが、100%にすれば良いんです（ダメ）。v-modelでBranchが100%に出来なかった、、だれか教えてください。。

```js:jest.config.js
module.exports = {
    ...
    coveragePathIgnorePatterns: ['xxx.vue']
}
```
モックはJsonをそのまま返しちゃえ

```c#
var result = JsonSerializer.Deserialize<dynamic>(jsonString);
return Ok(result);
```
storybookに全てのコンポーネントを反映しなくてよいよね
<img width="587" alt="スクリーンショット 2020-01-15 22.56.57.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496802/7f4fff52-ffc0-66a8-a2c0-faf3d8dcba32.png">

# 一通りvueを味わってみて
簡単と言われていましたが、普通に難しかったです。SPAしか開発したことがなかったのでCSRとSSRがわかってなかったことを痛感しました。でもNuxtJS面白いのでおすすめです。

一応[Github](https://github.com/ikuosaito1989/profile-frontend)公開しています