DockerでNuxt.jsの環境構築を行う

## はじめに
### 今回の記事で作成できる環境
<ul>
<li>Nuxt.js</li>
<li>TypeScript</li>
<li>Composition-API</li>
<li>Storybook</li>
</ul>

この環境を7つのコマンドで作成できるようにしました。

## Github

https://github.com/ssk9597/Docker_Nuxtjs_TypeScript_CompositionApi_Storybook_Clone

一応3つのSTEPでやっていきます。

<ul>
<li>Nuxt.js内でSassが使えるようにする</li>
<li>TypeScriptとComposition-APIを導入してAPIを叩く</li>
<li>Storybookを導入して公式サイトの`Link`を作ってみる</li>
</ul>

## [Step1] Nuxt.js内でSassが使えるようにする
### クローン
```terminal:ターミナル
$ git clone git@github.com:ssk9597/Docker_Nuxtjs_TypeScript_CompositionApi_Storybook_Clone.git

// ディレクトリに移動する
$ cd Docker_Nuxtjs_TypeScript_CompositionApi_Storybook_Clone
```

### Nuxt.jsの実行
```terminal:ターミナル
$ make nuxt
```

私は、`npx create-nuxt-app`に関しては以下のように入力していきました。

```
❯ make nuxt            
npx create-nuxt-app frontend

create-nuxt-app v3.6.0
✨  Generating Nuxt.js project in frontend
? Project name: frontend
? Programming language: TypeScript
? Package manager: Yarn
? UI framework: None
? Nuxt.js modules: (Press <space> to select, <a> to toggle all, <i> to invert selection)
? Linting tools: (Press <space> to select, <a> to toggle all, <i> to invert selection)
? Testing framework: None
? Rendering mode: Universal (SSR / SSG)
? Deployment target: Server (Node.js hosting)
? Development tools: (Press <space> to select, <a> to toggle all, <i> to invert selection)
? What is your GitHub username? ryo
? Version control system: Git
```

### ファイルの修正を行う
#### frontend/nuxt.config.js
```diff_javascript:frontend/nuxt.config.js
export default {
  // Global page headers: https://go.nuxtjs.dev/config-head
  head: {
    title: 'frontend',
    htmlAttrs: {
-     lang: 'en'
+     lang: 'ja'
    },
    meta: [
      { charset: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' },
      { hid: 'description', name: 'description', content: '' }
    ],
    link: [
      { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }
    ]
  },

  // Global CSS: https://go.nuxtjs.dev/config-css
  css: [
  ],

  // Plugins to run before rendering page: https://go.nuxtjs.dev/config-plugins
  plugins: [
  ],

  // Auto import components: https://go.nuxtjs.dev/config-components
  components: true,

  // Modules for dev and build (recommended): https://go.nuxtjs.dev/config-modules
  buildModules: [
    // https://go.nuxtjs.dev/typescript
    '@nuxt/typescript-build',
  ],

  // Modules: https://go.nuxtjs.dev/config-modules
  modules: [
+   '@nuxt/http'
  ],

  // Build Configuration: https://go.nuxtjs.dev/config-build
  build: {
  }
}
```

#### frontend/pages/index.vue
こちらはそのままペーストしてください。
sassが使えるようになっているか確認しましょう。


```vue:frontend/pages/index.vue
<template>
  <div class="container">
    <p>ボタンをホバーすると透過されます</p>
    <button class="button">ボタン</button>
  </div>
</template>

<style lang="scss" scoped>
.container {
  margin: 24px;
}

.button {
  padding: 8px 16px;
  color: #fff;
  background-color: red;
  border: none;

  &:hover {
    opacity: 0.7;
    cursor: pointer;
  }
}
</style>
```

それでは確認してみましょう。
ホバーしたとき透過されましたか？
透過されればOKです。

![スクリーンショット 2022-03-06 16.43.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838372/c39920df-0a50-cbee-9863-456526fd43f4.png)

### nuxt.config.jsをTypeScriptに変換
#### frontend/nuxt.config.ts
```diff_typescript:frontend/nuxt.config.ts
import { NuxtConfig } from '@nuxt/types';

const config: NuxtConfig = {
  // Global page headers: https://go.nuxtjs.dev/config-head
  head: {
    title: 'frontend',
    htmlAttrs: {
      lang: 'ja',
    },
    meta: [
      { charset: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' },
      { hid: 'description', name: 'description', content: '' },
    ],
    link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }],
  },

  // Global CSS: https://go.nuxtjs.dev/config-css
  css: [],

  // Plugins to run before rendering page: https://go.nuxtjs.dev/config-plugins
  plugins: [],

  // Auto import components: https://go.nuxtjs.dev/config-components
  components: true,

  // Modules for dev and build (recommended): https://go.nuxtjs.dev/config-modules
  buildModules: [
    // https://go.nuxtjs.dev/typescript
    '@nuxt/typescript-build',
  ],

  // Modules: https://go.nuxtjs.dev/config-modules
  modules: ['@nuxt/http'],

  // Build Configuration: https://go.nuxtjs.dev/config-build
  build: {},
};

export default config;
```


## [Step2] TypeScriptとComposition-APIを導入してAPIを叩く
### TypeScriptの導入
```terminal:ターミナル
$ make typescript
```

### ファイルの修正
#### frontend/shims-vue.d.ts
```typescript:frontend/shims-vue.d.ts
declare module '*.vue' {
  import Vue from 'vue';
  export default Vue;
}
```

#### frontend/tsconfig.json
```json:frontend/tsconfig.json
{
  "compilerOptions": {
    "target": "ES2018",
    "module": "ESNext",
    "moduleResolution": "Node",
    "lib": ["ESNext", "ESNext.AsyncIterable", "DOM"],
    "esModuleInterop": true,
    "allowJs": true,
    "sourceMap": true,
    "strict": true,
    "noEmit": true,
    "experimentalDecorators": true,
    "baseUrl": ".",
    "paths": {
      "~/*": ["./*"],
      "@/*": ["./*"]
    },
    "types": ["@nuxt/types", "@types/node"]
  },
  "files": ["shims-vue.d.ts"],
  "include": [
    "components/**/*.ts",
    "components/**/*.vue",
    "layouts/**/*.ts",
    "layouts/**/*.vue",
    "pages/**/*.ts",
    "pages/**/*.vue"
  ],
  "exclude": ["node_modules", ".nuxt", "dist"]
}
```

### Composition-APIの導入
```terminal:ターミナル
$ make composition-api
```

### ファイルの修正
#### frontend/nuxt.config.js
```diff_javascript:frontend/nuxt.config.js
export default {
+ // env
+ publicRuntimeConfig: {
+   API_URL: process.env.API_URL,
+ },
  // Global page headers: https://go.nuxtjs.dev/config-head
  head: {
    title: 'frontend',
    htmlAttrs: {
      lang: 'ja'
    },
    meta: [
      { charset: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' },
      { hid: 'description', name: 'description', content: '' }
    ],
    link: [
      { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }
    ]
  },

  // Global CSS: https://go.nuxtjs.dev/config-css
  css: [
  ],

  // Plugins to run before rendering page: https://go.nuxtjs.dev/config-plugins
  plugins: [
  ],

  // Auto import components: https://go.nuxtjs.dev/config-components
  components: true,

  // Modules for dev and build (recommended): https://go.nuxtjs.dev/config-modules
  buildModules: [
    // https://go.nuxtjs.dev/typescript
    '@nuxt/typescript-build',
+   '@nuxtjs/composition-api'
  ],

  // Modules: https://go.nuxtjs.dev/config-modules
  modules: [
    '@nuxt/http'
  ],

+ http: {
+   baseURL: process.env.API_URL,
+   browserBaseURL: process.env.API_URL,
+ },

  // Build Configuration: https://go.nuxtjs.dev/config-build
  build: {
  }
}
```

#### frontend/.env

```env:frontend/.env
API_URL=https://jsonplaceholder.typicode.com/users/1
```

#### frontend/pages/index.vue
```vue:frontend/pages/index.vue
<template>
  <div>
    <p>
      {{ users }}
    </p>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref, useContext, useFetch } from '@nuxtjs/composition-api';

export default defineComponent({
  setup() {
    const { $http } = useContext();

    // data
    const users = ref({});

    useFetch(async () => {
      users.value = await $http.$get('/');
    });

    return {
      users,
    };
  },
});
</script>
```

以下のように表示されればOKです。

![スクリーンショット 2022-03-06 17.04.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838372/e8277542-d23b-00db-ad71-edcb0b89fbe3.png)

コードに関して、「何書いているかさっぱりわからん」という方は以下をご確認ください。

https://ics.media/entry/210120/

https://http.nuxtjs.org/

## [Step3] Storybookを導入して公式サイトの`Link`を作ってみる
### Storybookの導入
```terminal:ターミナル
$ make storybook
```

### ファイルの修正
#### frontend/nuxt.config.js
```diff_javascript:frontend/nuxt.config.js
export default {
  // env
  publicRuntimeConfig: {
    API_URL: process.env.API_URL,
  },
  // Global page headers: https://go.nuxtjs.dev/config-head
  head: {
    title: 'frontend',
    htmlAttrs: {
      lang: 'ja',
    },
    meta: [
      { charset: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' },
      { hid: 'description', name: 'description', content: '' },
    ],
    link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }],
  },

  // Global CSS: https://go.nuxtjs.dev/config-css
  css: [],

  // Plugins to run before rendering page: https://go.nuxtjs.dev/config-plugins
  plugins: [],

  // Auto import components: https://go.nuxtjs.dev/config-components
  components: true,

  // Modules for dev and build (recommended): https://go.nuxtjs.dev/config-modules
  buildModules: [
    // https://go.nuxtjs.dev/typescript
    '@nuxt/typescript-build',
    '@nuxtjs/composition-api/module',
  ],

  // Modules: https://go.nuxtjs.dev/config-modules
  modules: ['@nuxt/http', '@nuxtjs/proxy'],

  proxy: {
    '/api': process.env.API_URL,
  },

  http: {
    baseURL: process.env.API_URL,
    browserBaseURL: process.env.API_URL,
  },

  // Build Configuration: https://go.nuxtjs.dev/config-build
  build: {},

+ storybook: {
+   port: 6006
+ },
};
```

#### frontend/.gitignore
必要ないファイルがGithubへプッシュされないようにするため以下の追記を行います。
適当に最終行にでもコピペしといてください。

https://storybook.nuxtjs.org/getting-started/installation

```
# storybook
.nuxt-storybook
storybook-static
```

### Storybookの起動
```terminal:ターミナル
$ make launch-storybook
```

### 公式サイトを参考に`Link.vue`と`Link.stories.js`を作成していく
公式サイトはこちらです。

https://storybook.nuxtjs.org/getting-started/usage

#### frontend/components/Link.vue
```vue:frontend/components/Link.vue
<template>
  <nuxt-link to="https://nuxtjs.org">
    NuxtJs
  </nuxt-link>
</template>
```

#### frontend/components/Link.stories.js
```javascript:frontend/components/Link.stories.js
export default {
  title: 'Link'
}

export const NuxtWebsite = () => '<Link />'
```

これで完成です。

![スクリーンショット 2022-03-06 17.44.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838372/4409ff96-7947-6933-164b-1ba1ecfab094.png)