軽くNuxtJSを触る

# やること

* NuxtJSインストール
* ページの追加
* storeの確認
* propsとslotの確認
* $emitの確認
* middlewareの確認
* layoutの確認

# コード

[yoshida-san/nuxt-sample](https://github.com/yoshida-san/nuxt-sample)

# プロジェクト作成

```sh
~$ npx nuxt-create-app nuxt-sample
```

設定内容は以下の通り。

```sh
? Programming language: JavaScript
? Package manager: Npm
? UI framework: None
? Nuxt.js modules: Axios - Promise based HTTP client
? Linting tools: ESLint, Prettier
? Testing framework: Jest
? Rendering mode: Single Page App
? Deployment target: Server (Node.js hosting)
? Development tools: jsconfig.json, Dependabot
? Continuous integration: None
? Version control system: Git
```

動かしてみる（当然のように動く）。

```sh
~$ cd nuxt-sample
~$ npm run dev
```

ホットリロードは便利。

# 構成確認

初期状態は以下の通り。
ファイルには先頭に📄を付加してます。
各ディレクトリとファイルの説明は[ディレクトリ構成 - NuxtJS](https://ja.nuxtjs.org/docs/2.x/get-started/directory-structure)と[.nuxt - NuxtJS](https://ja.nuxtjs.org/docs/2.x/directory-structure/nuxt)に書いてある。

```txt
rootDir
 ├ .github
 │  └ 📄dependabot.yml
 ├ assets
 ├ components
 ├ layouts
 ├ middleware
 ├ node_modules
 ├ pages
 ├ plugins
 ├ static
 ├ store
 ├ test
 ├ 📄.babelrc
 ├ 📄.editorconfig
 ├ 📄.eslintrc.js
 ├ 📄.gitignore
 ├ 📄.prettierrc
 ├ 📄jest.config.js
 ├ 📄jsconfig.json
 ├ 📄nuxt.config.js
 ├ 📄package-lock.json
 ├ 📄package.json
 └ 📄README.md
```

middlewareに関してはexpressのmiddleware的な感じ？
pluginsは普通にロジックを切り出して置いておくディレクトリですかね。

# ページを追加する

Nuxtはvue-routerをいい感じにラップしてくれているのでページの追加は/pagesにany.vueを追加するだけです。`hoge.vue`を追加すれば`/hoge`でアクセスできます。`/hoge/index.vue`のようにディレクトリを切ってindex.vueを用意する形でもOK。
.vueからstyleやscriptを切り離すならディレクトリを作成した方が良さそうな印象（scriptも共通処理はpluginsに入ると思いますが）。

# Vuterのインストール

VSCodeでコードを書くのでVuterをインストール。
最新バージョンをインストールしたらformatOnSave設定と何か干渉しているのかいつまでも保存が終わらない...[Getting code actions from ''Vetur', 'Eslint'' takes too long!](https://github.com/vuejs/vetur/issues/2150)を参照してversionを0.25.0に下げることで解決。

先人の知恵はありがたい。

# store

store確認用のインクリメントボタン、デクリメントボタン、テキストのatomを用意してそれっぽいcomponentを作ってみる。storeも作る。

`index.vue`

```vue
<template>
  <div class="container">
    <section>
      <Counter />
    </section>
  </div>
</template>

<script>
export default {
  // logic
}
</script>

<style scoped>
.container {
  margin: 0 auto;
  min-height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
  text-align: center;
}
</style>
```

`/components/atoms/CounterLabel.vue`

```vue
<template>
  <label>{{ $store.state.counter }}</label>
</template>
```

`/components/atoms/IncrementButton.vue`

```vue
<template>
  <button @click="increment">+</button>
</template>

<script>
export default {
  methods: {
    increment() {
      this.$store.commit('increment')
    },
  },
}
</script>
```

`/components/atoms/DecrementButton.vue`

```vue
<template>
  <button @click="decrement">-</button>
</template>

<script>
export default {
  methods: {
    decrement() {
      this.$store.commit('decrement')
    },
  },
}
</script>
```

`/components/Counter.vue`

```vue
<template>
  <div>
    <div>
      <CounterLabel />
    </div>
    <div>
      <IncrementButton />
      <DecrementButton />
    </div>
  </div>
</template>
```

`/store/index.js`

```js
export const state = () => ({
  counter: 0,
})

export const mutations = {
  increment(state) {
    state.counter++
  },
  decrement(state) {
    state.counter--
  },
}
```

できた（気がする

# props / slot

propsとslotの使い分けとかは割愛。

`/components/atoms/IncrementButton.vue`

```vue
<template>
  <button @click="increment">{{ text }}</button>
</template>

<script>
export default {
  props: {
    text: {
      type: String,
      default: 'Increment',
    },
  },
  methods: {
    increment() {
      this.$store.commit('increment')
    },
  },
}
</script>
```

`/components/atoms/DecrementButton.vue`

```vue
<template>
  <button @click="decrement"><slot /></button>
</template>

<script>
export default {
  methods: {
    decrement() {
      this.$store.commit('decrement')
    },
  },
}
</script>
```

`/components/Counter.vue`

```vue
<template>
  <div>
    <div>
      <CounterLabel />
    </div>
    <div>
      <IncrementButton text="+" />
      <DecrementButton><slot>Decrement</slot></DecrementButton>
    </div>
  </div>
</template>
```

slotを複数使う時は名前付きslotを使いましょうねと。

# $emit
Child Componentの値やイベントを親に渡す時に使う。値はstoreで管理すれば良いし、イベントはmethodを渡せば良いのでは？？？って思ってるけど多分何か違うんだろうなぁ...。
一気に見通しが悪くなった気がするけど慣れてないだけかな...。

`/components/Counter.vue`

```vue
<template>
  <div>
    <div>
      <CounterLabel />
    </div>
    <div>
      <IncrementButton text="+" @welcome="say" />
      <DecrementButton><slot>Decrement</slot></DecrementButton>
      <SayButton @sayCount="say" />
    </div>
  </div>
</template>

<script>
export default {
  methods: {
    say(val) {
      alert(val)
    },
  },
}
</script>
```

`/components/atoms/SayButton.vue`

```vue
<template>
  <button @click="sayCount">Say</button>
</template>

<script>
export default {
  methods: {
    sayCount() {
      this.$emit('sayCount', this.$store.state.counter)
    },
  },
}
</script>
```

# middleware

レンダリング前に処理をしたい場合はmiddlewareを使う。middlewareディレクトリ内に処理を定義して、pagesディレクトリ内の.vueでどの処理を適用させるか定義する。  

`/middleware/sampleMiddleware.js`

```js
export default function (context) {
  console.log('middleware!')
  console.dir(context)
}
```

`/pages/index.vue`

```vue
<template>
  <div class="container">
    <section>
      <Counter />
    </section>
  </div>
</template>

<script>
export default {
  middleware: 'sampleMiddleware',
}
</script>
```

# layout

Webページのレイアウトを定義できる。pagesディレクトリ内の.vueでどのレイアウトを使用するか定義できる。middlewareと組み合わせてあれこれできそう。

`/layout/default.vue`

```vue
<template>
  <div>
    <h1>default.vue</h1>
    <Nuxt />
  </div>
</template>

<style>
:
</style>
```


`/layout/sampleLayout.vue`

```vue
<template>
  <div>
    <h1>sampleLayout.vue</h1>
    <Nuxt />
  </div>
</template>

<style>
:
</style>
```

`/pages/index.vue`

```vue
<template>
  <div class="container">
    <section>
      <Counter />
    </section>
  </div>
</template>

<script>
export default {
  layout: 'sampleLayout',  // switch layout
  middleware: 'sampleMiddleware',
}
</script>

<style scoped>
:
</style>
```

# おわり

一息入れたらnamespaceやmodulesも触っておこうと思います。