便利にNuxt開発をするために準備していること

# 前書き
個人的にNuxt開発する際の準備Todoを清書しました。
目次から気になるTopicだけみていってください:smiley:

AtomicDesgin & Storybookのセットはコーディングしていて本当に気持ちいいですね:sunny:

## github
以下のすべての設定を行ったrepositoryをいかにおいておきます。
forkしてshを実行していただければご利用いただけます。

https://github.com/wataboru/nuxt-initial-setting-completed

# プロジェクト作成
何はともあれ、とりあえずプロジェクトを作ります。
`npx create-nuxt-app`で作成します。

```bash
$ npx create-nuxt-app sample-nuxt-project

create-nuxt-app v2.15.0
✨  Generating Nuxt.js project in sample-nuxt-project
? Project name sample-nuxt-project
? Project description My slick Nuxt.js project
? Author name Wataru@Boru
? Choose programming language JavaScript
? Choose the package manager Yarn
? Choose UI framework None
? Choose custom server framework None (Recommended)
? Choose Nuxt.js modules Axios
? Choose linting tools ESLint, Prettier
? Choose test framework None
? Choose rendering mode Single Page App
? Choose development tools None
<省略>
```

# SCSSの導入
BEM形式でCSSを記載していくため、便利に使えるSCSSを導入します。

```bash
yarn add sass-loader node-sass sass-resources-loader
```

```package.json(実行後_抜粋)
  "dependencies": {
    "@nuxtjs/axios": "^5.3.6",
    "node-sass": "^4.14.0",
    "nuxt": "^2.0.0",
    "sass-loader": "^8.0.2"
  },
```

- 参考
    - [Nuxt.jsのプロジェクトを作成してSCSSを導入するまで](https://www.suzu6.net/posts/173-nuxt-scss/)


# リセットCSSを適応
Webブラウザの初期Styleはリセットして、ストレスフリーにスタイリングしましょう！

1. ディレクトリ作成  
`mkdir -p styles`
2. お好きなリセットCSSを取得して、上記ディレクトリに格納  
`cp -p ~/hoge/reset.scss styles/`   
3. `nuxt.config.js`へ追記

```nuxt.config.js(抜粋)
  /*
   ** Global CSS
   */
  css: [{ src: '@/styles/reset.scss', lang: 'scss' }],
```


# AtomicDesign用のディレクトリ作成

AtomicDesignとは？という方はざっくりこの辺がわかりやすいかもしれません。
[Atomic Design を分かったつもりになる | DeNA DESIGN BLOG](https://design.dena.com/design/atomic-design-%E3%82%92%E5%88%86%E3%81%8B%E3%81%A3%E3%81%9F%E3%81%A4%E3%82%82%E3%82%8A%E3%81%AB%E3%81%AA%E3%82%8B/)

- `/components`配下に以下作成
  - atoms
  - molecules
  - organisms
  - pages
  - (templatesについては、`/layouts`を利用)

- コマンド

```bash
mkdir -p components/atoms components/molecules components/organisms components/pages
```

# storybookの導入
AtomicDesignによる実装に非常に効果的なStorybookを導入して、便利にコンポーネントを作っていきましょう！

![atoms___ContentHeading_-_Test_⋅_Storybook.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317515/6883c4dc-2c89-e70e-2fdc-45443015cb01.png)

### 導入手順

- まずはyarnでimport  
`yarn add -D @storybook/vue`

- `pagkage.json`にscriptを追記して楽にする

```package.json(抜粋)
  "scripts": {
    "storybook": "start-storybook -p 9000 -s ./dist"
  },
```
  
- storybook用のフォルダを作成  
`mkdir .storybook`
- SCSSを効かせるためのファイルを作成
`vim .storybook/Decorator.vue`

```.storybook/Decorator.vue
<template>
  <div class="decoarator">
    <slot name="story"></slot>
  </div>
</template>

<script>
export default {
  name: 'Decorator'
}
</script>

<style lang="scss">
@import '@/styles/reset.scss';
</style>
```

- viewport-addonを導入（SPサイズなどで検証するのに利用）  
`yarn add -D @storybook/addon-viewport babel-preset-vue`
- actions-addonを導入（nuxt-linkの検査などに利用）  
`yarn add -D @storybook/addon-actions`
- configファイルを作成  
`vim .storybook/config.js`
  - Decoratorの設定
  - @storybook/addon-viewportの設定
  - @storybook/addon-actionsの設定

```.storybook/config.js
import Vue from 'vue'
import { addDecorator, addParameters } from '@storybook/vue'
import Decorator from './Decorator.vue'
import { INITIAL_VIEWPORTS } from '@storybook/addon-viewport'
import { action } from '@storybook/addon-actions'

/* nuxt-linkをstorybook上で検査できるようにする */
Vue.component('nuxt-link', {
  props:   ['to'],
  methods: {
    log() {
      action('link target')(this.to)
    },
  },
  template: '<a href="#" @click.prevent="log()"><slot>NuxtLink</slot></a>',
})

/* 共通SCSSを呼び出す */
addDecorator(() => ({
  components: { Decorator },
  template: `
    <decorator>
      <story slot="story"/>
    </decorator>
  `
}))

/* ViewPortプラグインの初期設定 */
addParameters({
  viewport: {
    viewports: INITIAL_VIEWPORTS,
    defaultViewport: 'iphonex'
  }
})

```

- main.jsを作成し、storiesファイルのパスを指定。  
`vim .storybook/main.js`

```.storybook/main.js
/*
 storybook fileの場所を正規表現で指定
 vueファイルとstories.jsファイルを同フォルダに配置する場合
 */
module.exports = {
  stories: ['../components/**/*.stories.[tj]s']
}
```

- 利用するWeb fontがあれば、preview-head.htmlを作成  
`vim .storybook/preview-head.html`

```.storybook/preview-head.html
<!-- Adobe Fontsなど、フォントファイルをlink形式でstorybookに効かせたい場合は本ファイルを作成 -->
<link rel="stylesheet" href="https://use.typekit.net/hogehoge.css" />
```

- アドオン適用のため、addons.jsを作成  
`vim .storybook/addons.js`

```.storybook/addons.js
import '@storybook/addon-viewport/register'
import '@storybook/addon-actions/register'
```

- webpack.config.jsを作成  
`vim .storybook/webpack.config.js`

```.storybook/webpack.config.js
const path = require('path')

/* SCSSを利用できるようにloaderを設定
  (注意) nuxt側とは別で、改めてこちらのwebpack.configを設定する必要がある。
  */
module.exports = ({ config }) => {
  config.resolve.alias['@'] = path.resolve(__dirname, '../')
  config.resolve.alias['~'] = path.resolve(__dirname, '../')
  config.module.rules.push({
    test: /\.scss$/,
    loaders: [
      'style-loader',
      'css-loader',
      'sass-loader',
    ]
  })
  return config
}
```

- テストのため、atomsにディレクトリを作成しファイルを配置
  - `mkdir -p components/atoms/Title`

```components/atoms/Title/index.vue
<template>
  <div class="title">
    <slot />
  </div>
</template>

<style lang="scss" scoped>
.title {
  font-size: 40px;
  color: #000;
}
</style>
```

```components/atoms/Title/index.stories.js
import StoryComponent from './index'

export default {
  title: 'atoms/Title'
}

export const normal = () => ({
  components: { StoryComponent },
  template: `<story-component>Title</story-component>`
})
```

- storybookを起動する  
`yarn run storybook`

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317515/b71416d7-1960-4267-ab53-ed69fb729ba6.png)

# その他、必要に応じて導入する

- [Moment.js](https://momentjs.com/)
  - `yarn add moment`
- [Lodash.js](https://lodash.com/)
  - `yarn add lodash`
- その他プラグイン周りも必要に応じて導入する

- GoogleAnalytics [参考:公式Doc](https://ja.nuxtjs.org/faq/google-analytics/)
  - `yarn add @nuxtjs/google-analytics `
  - `plugins/ga.js`を作成し、`nuxt.config.js`に追記

```plugins/ga.js
/* eslint-disable */

export default ({ app }) => {
  /*
  ** クライアントサイドかつプロダクションモードでのみ実行
  */
  if (process.env.NODE_ENV !== 'production') return
  /*
  ** Google アナリティクスのスクリプトをインクルード
  */
  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
  })(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
  /*
  ** 現在のページをセット
  */
  ga('create', 'UA-XXXXXXXX-X', 'auto')
  /*
  ** ルートが変更されるたびに毎回実行（初期化も実行される）
  */
  app.router.afterEach((to, from) => {
    /*
    ** Google アナリティクスにページビューが追加されたことを伝える
    */
    ga('set', 'page', to.fullPath)
    ga('send', 'pageview')
  })
}
```

```nuxt.config.js(抜粋)
  plugins: [
    { src: '~plugins/ga.js', mode: 'client' }
  ]
```