Nuxt.js(v2.6.x)やっておきたい設定：その3/3｜コンポーネントフィアルの管理方法とloading・各種トランジションの設定

本稿は「Nuxt.js(v2.6.x)やっておきたい設定シリーズ」の第３段です。

- [その1/3｜create-nuxt-app〜各種プラグインの導入まで。](https://qiita.com/amishiro/items/1c2a009cb0470cf3223a)
- [その2/3｜SEOに必要なmetaとogpの設定及びpwa(Manifest、Icon、Workbox)の設定](https://qiita.com/amishiro/items/e668be423a85c2b61696)
- [その3/3｜コンポーネントフィアルの管理方法とloading・各種トランジションの設定](https://qiita.com/amishiro/items/1f768628e3b7267466c2)

---

「Nuxt.jsやっておきたい設定：その3/3」として、コンテンツ詳細の作成に入る前に決めておきたい「コンポーネントファイルの管理方法」と「loading・各種トランジションの設定」をまとめる。
プロジェクトチームの趣味趣向に合わせて変更を検討すべきことだが、一つの基準があったほうが話が進みやすいと思う。

すこしでも手助けになれば。

## コンポーネントファイルの管理方法

コンポーネントファイルのファイル名の先頭に、`-`（[ignorePrefix](https://ja.nuxtjs.org/api/configuration-ignore/)）を必ずつける。すると、ファイル名にかかわらずなんのファイルか判断することができる。もちろんわかりやすい名前にしたほうが良いのだが…なかなか難しいので。

詳細は、[nuxt.jsにおけるコンポーネントの規約（案）その２：ignorePrefixプロパティ版](https://qiita.com/amishiro/items/721811169a589b3cd872)を読んでほしい。

設定内容だけ記載する。

## componentsディレクトリ内のファイルを一括登録＆呼び出し

plugins経由で登録を行うことで、どこからでも呼び出せるようになるのでcomponents.jsを作成する。

```javascript:plugins/components.js
import Vue from 'vue'

import UButton from '~/components/-ui-button'
import UFigure from '~/components/-ui-figure'
import MHeading from '~/components/-main-heading'

Vue.mixin({
  components: {
    UButton,
    UFigure,
    MHeading,
  }
})
```

components.jsをnuxt.confing.jsに登録

```javascript:nuxt.config.js
module.exports = {
  plugins: [
    { src: '~plugins/components' },
    ...
  ],
}
```

毎回importしなくても呼び出せるようになる。

```vue:test.vue
<template lang="pug">
div
  m-heading ページ名
  u-button(to="/") ボタン
</template>
```

OK。

## トランジションの種類

Nuxt.jsが基本機能を設定、また、自作して設置しておく。ある程度のインタラクティブなサイトを構築できる想定だ。具体的には以下５つ。

- 初回アクセス時のローディング（自作）
- loading（Nuxt.js基本機能）
- ページ推移トランジション（Nuxt.js基本機能）
- ページ推移時のコンポーネントのトランジション（自作）
- パーツのトランジション（参考までに）

## 初回アクセス時のローディング（自作）

どれだけチューンナップしても初回アクセス時の表示が遅くなることがある。ならば、できるだけユーザーに楽しんでもらうことに方向を切り替えるのも一つの手だ。

ならば、初めから仕込んでおく。

### layout内に-first-loading.vueを設置

まず気づいただろうか…。ファイル名の先頭に`-`(ignorePrefix)がついてるだけで、コンポーネントファイルを作成してるとわかる。これが、規約の素晴らしさ。そしてlayout内に設置したということは、もちろんlayout内のdifult.vueに追加していくこととなる。

```layout/-first-loading.vue
<template lang="pug">
div
  .loading(:class="{ 'is-finish': dataLoadFinish }")
    p loading...
</template>

<script>
export default {
  data() {
    return {
      dataLoadFinish: false
    }
  },
  mounted() {
    window.addEventListener('load', () => {
      this.dataLoadFinish = true
    })
  }
}
</script>

<style lang="scss" scoped>
.loading {
  position: fixed;
  z-index: 3;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100vw;
  height: 100vh;
  background: $primary;
  color: $white;
  &.is-finish {
    transition: opacity 1s, z-index 0s 1.01s;
    opacity: 0;
    z-index: -1;
  }
}
</style>
```


### difult.vueに-first-loading.vueを追加

```layout/difult.vue
<template lang="pug">
div.layout
  firstLoading
  nuxt
</template>

<script>
import firstLoading from './-first-loading'
export default {
  components: {
    firstLoading,
  }
}
</script>

<style lang="scss" scoped>
.layout {
  position: relative;
}
</style>
```

以上だ。ある程度なれている人ならば、そんなに難しくないはず。

## loading（Nuxt.js基本機能）

Nuxt.jsの基本機能のままシンプルでよい（と思う）。なぜならばprefetch機能が充実したNuxt.jsなら殆ど表示されない。すばらしい。

APIの呼び出し方法に注意しつつ、色味を変更する程度でいいだろう。

```nuxt.config.js
module.exports = {
  ...
  loading: { color: '#0A428C' },
}
```


### mouted内でAPIを呼び出ししている場合は注意

loadingが表示されるのは、体外がAPIを呼び出すときだ。syncDataやfetchで指定している場合は、ディフォルトのloading設定がうまくやってくれる。

しかし…

mouted内でAPIを呼び出しする場合にはひと手間が必要。

```pages/loading-test-in-mounted.vue
<template lang="pug">
div
  u-button(to="/") index
  p(v-if="!json") 呼び出し中
  pre(v-else) {{json}}
</template>

<script>
export default {
  loading: false, // ←これ忘れがちなので入れる。
  data() {
    return {
      json: null
    }
  },
  async mounted() {
    this.json = await this.$axios.$get('https://randomuser.me/api/')
    this.$nuxt.$loading.finish()
  }
}
</script>
```

参考：[公式サイト- loading プロパティ](https://ja.nuxtjs.org/api/configuration-loading)

ちゃんと呼び出されるまでAPIの呼び出しが完了するまでローディングが続いてくれる。

OK。

## ページ推移トランジション（Nuxt.js基本機能）

次はページ推移時のトランジション。これもシンプルでいいと思う。

### まずは、トランジションscssを作成

[AOS](https://michalsnik.github.io/aos/)を参考にトランジションscssを作成。

```asetts/css/transition/index.scss
.fade {
  &-leave-active {
    transition: all .8s cubic-bezier(1.0, 0.5, 0.8, 1.0);
  }
  &-leave-to {
    opacity: 0;
  }
  &-enter {
    opacity: 0;
  }
  &-enter-active {
    transition: all .6s ease;
  }
}

.fade-up {
  &-leave-active {
    transition: all .8s cubic-bezier(1.0, 0.5, 0.8, 1.0);
  }
  &-leave-to {
    transform: translateY(-10px);
    opacity: 0;
  }
  &-enter {
    transform: translateY(10px);
    opacity: 0;
  }
  &-enter-active {
    transition: all .6s ease;
  }
}

.fade-down {
  &-leave-active {
    transition: all .8s cubic-bezier(1.0, 0.5, 0.8, 1.0);
  }
  &-leave-to {
    transform: translateY(10px);
    opacity: 0;
  }
  &-enter {
    transform: translateY(-10px);
    opacity: 0;
  }
  &-enter-active {
    transition: all .6s ease;
  }
}

.fade-right {
  &-leave-active {
    transition: all .8s cubic-bezier(1.0, 0.5, 0.8, 1.0);
  }
  &-leave-to {
    transform: translateX(-10px);
    opacity: 0;
  }
  &-enter {
    transform: translateX(10px);
    opacity: 0;
  }
  &-enter-active {
    transition: all .6s ease;
  }
}

.fade-left {
  &-leave-active {
    transition: all .8s cubic-bezier(1.0, 0.5, 0.8, 1.0);
  }
  &-leave-to {
    transform: translateX(10px);
    opacity: 0;
  }
  &-enter {
    transform: translateX(-10px);
    opacity: 0;
  }
  &-enter-active {
    transition: all .6s ease;
  }
}

.zoom-in {
  &-leave-active {
    transition: all .8s cubic-bezier(1.0, 0.5, 0.8, 1.0);
  }
  &-leave-to {
    transform: scale(.9);
    opacity: 0;
  }
  &-enter {
    transform: scale(.9);
    opacity: 0;
  }
  &-enter-active {
    transition: all .6s ease;
  }
}
```

nuxt.config.jsから呼び出す。

```nuxt.config.js
module.exports = {
  css: [
    ...
    // Original
    '~/assets/css/transition/index.scss',
  ],
}
```

## 使いたいトランジションを選ぶ。

ページ推移用のトランジションは２種類あることに注意したい。

- transition（通常のページ推移時のトランジション）
- layoutTransition（layoutの変更があった場合のトランジション）

両方とも設定しておく。

```nuxt.config.js
module.exports = {
  ...
  transition: {
    name: 'fade-left',
    mode: 'out-in'
  },
  layoutTransition: {
    name: 'fade-right',
    mode: 'out-in'
  },
}
```

動作確認

```
npm run dev
```

OK。

## ページ推移時のコンポーネントのトランジション（自作）

[ページ移動時のフックの設定](https://coliss.com/articles/build-websites/operation/javascript/native-like-animations-for-page-transitions.html)
を参考に、ページの変更を検知できるようにする。

### sotreの設置

当時よりnuxt.jsが優秀になっているのでモジュールモードでシンプルに作成できる。

```store/pages.js
// state
export const state = () => ({
  page: 'index'
})

// mutations
export const mutations = {
  updatePage(state, pageName) {
    state.page = pageName
  }
}
```

### middlewareの設置

ここは、当時から変わんないかな。

```middleware/pages.js
export default function (context) {
  // go tell the store to update the page
  context.store.commit('pages/updatePage', context.route.name)
}
```

## middlewareを読み込ませる

nuxt.config.jsに読み込ませる。

```nuxt.config.js
module.exports = {
  router: {
    ...
    middleware: 'pages',
  },
}
```

## -main-heading.vueで試してみる。

```components/-main-heading.vue
<template lang="pug">
div
  transition(name="zoom-in" mode="out-in")
    .m-heading(v-show="enter")
      p {{ page }}-
      slot
</template>

<script>
export default {
  data() {
    return {
      enter: false
    }
  },
  computed: {
    page() {
      return this.$store.state.pages.page
    }
  },
  watch: {
    page() {
      this.enter = false
    }
  },
  mounted() {
    this.enter = true
  },
}
</script>

<style lang="scss" scoped>
.m-heading {
  background-color: $primary;
  display: flex;
  align-items: center;
  justify-content: center;
  color: $white;
  width: 100%;
  height: 200px;
}
</style>
```


```pages/test.vue
<template lang="pug">
div
  m-heading ページ名
  u-button(to="/") indexへ
</template>
```

OK。

ページ推移トランジションと同じタイミングで、コンポーネントのトランジションが動いた。
あとは、`transition(name="xxx")`を変えて遊ぶだけ。かなり細かいところまでトランジションが調整できるようになった。

## パーツのトランジション（参考までに）

ラスト。

vue本来のトランジションの使い方を３つほど復習する。

- name属性を変更してトランジションの動作を変える。
- mode属性を変更してトランジションの動作を整える。
- v-for使っているときは、`transition-group`を使う。`mode="out-in"`が効かないので工夫する。

```pages/sample.vue
<template lang="pug">
div
  m-heading sample
  .content
    .section
      u-button(to="/") index
      hr
      h2 name属性を変更してトランジションの動作を変える。
      h3 fade-up
      transition(name="fade-up" mode="out-in")
        p(v-if="show1" key="1") {{show1}}
        p(v-else key="2") {{show1}}
      button.button(@click="show1 = !show1") toggle show
      h3 zoom-in
      transition(name="zoom-in" mode="out-in")
          p(v-if="show2" key="1") {{show2}}
          p(v-else key="2") {{show2}}
      button.button(@click="show2 = !show2") toggle show
      hr
      h2 mode属性を変更してトランジションの動作を整える。
      h3 mode="out-in" 有り
      div
        transition(name="fade-left" mode="out-in")
          p(v-if="show3" key="1") {{show3}}
          p(v-else key="2") {{show3}}
        button.button(@click="show3 = !show3") toggle show
      h3 mode="out-in" なし
      transition(name="fade-left")
          p(v-if="show3" key="1") {{show3}}
          p(v-else key="2") {{show3}}
      button.button(@click="show3 = !show3") toggle show
      hr
      h2 v-for使っているときは、`transition-group`を使う。`mode="out-in"`が効かないので工夫する。
      button.button(@click="show4 = !show4") toggle show
      transition-group.group(name="fade-left" tag="div")
        div.loading(v-if="!json" key="loading") 読み込み中だよ
        div.loading(v-if="show4 === false" key="stoping") 準備できたよ
        div(v-else key="wrapper")
          div(v-for="(v, k, i) in json.results[0]" :key="i")
            div {{ i }}
            pre {{ v }}
            br
</template>

<script>
export default {
  loading: false, // ←これ忘れがちなので入れる。
  data() {
    return {
      json: null,
      show1: false,
      show2: false,
      show3: false,
      show4: false,
      show5: false,
    }
  },
  async mounted() {
    this.json = await this.$axios.$get('https://randomuser.me/api/')
    this.$nuxt.$loading.finish()
  }
}
</script>

<style lang="scss" scoped>
.group {
  position: relative;
}
.loading {
  position: absolute;
}
</style>
```



---

お疲れ様でした。

これで、Nuxt.js(v2.6.x)やっておきたい設定 1〜3が完了。どのプロジェクトでも使える内容になってった…はず。

今まで設定してきた内容は、少し手を加えつつGitHubにアップした。

ということは？

・
・
・

amiTemplate-nuxt爆誕。（[GitHubページ](https://github.com/amishiro/AmiTemplate-NUXT)へ）