もがき系プログラマの日常

もがき系エンジニアの勉強したこと、日常のこと、気になっている技術、備忘録などを紹介するブログです。

Nuxt.jsビギナーズガイドを読んだ Vol.1

はじめに

こんにちは。

Kojirockの1人アドベントカレンダー Advent Calendar 2018の18日目の記事です。

体調が悪く1週間完全にダウンしていました。。。

アドベントカレンダーもまるまる抜けてしまって、残念極まりないです。

こういうときのために書き溜めしとかないとな、、、と思いました。

来年はこの反省を生かして必ず25日間書き続けられたらと思います。

で、今回からは、以下の本を読んで勉強できたことを記載していきます。

Nuxt.jsビギナーズガイド―Vue.js ベースのフレームワークによるシングルページアプリケーション開発

Nuxt.jsビギナーズガイド―Vue.js ベースのフレームワークによるシングルページアプリケーション開発

この本と同時に、 Nuxtの公式サイト も同時に確認しながら、手を動かしつつ進めていきました。

今回は、1章、2章のあたりを中心に勉強しました。

やってみた

1. インストール

本書では、前回のvueの本でも登場した Vue CLIでNuxt.jsのスターターテンプレートをインストールするという方法が紹介されていました。

今回は、公式サイトに紹介されている create-nuxt-app を使ってインストールしました。

$ mkdir nuxt
$ cd nuxt/
$ npx create-nuxt-app test_01
npx: 402個のパッケージを32.532秒でインストールしました。
> Generating Nuxt.js project in /path/to/study/nuxt/test_01
? Project name test_01 ← プロジェクト名
? Project description My exquisite Nuxt.js project ← プロジェクトの説明
? Use a custom server framework none ← サーバーサイドのフレームワークを選択
? Use a custom UI framework element-ui ← UI フレームワークの選択
? Choose rendering mode Universal ← SPAかuniversalモードかどうかの選択
? Use axios module yes ← axiosを使用するかどうかの選択
? Use eslint yes ← eslintを使用するかどうかの選択
? Use prettier yes ← prettierを使用するかどうかの選択
? Author name kojirock ← 作成者名
? Choose a package manager yarn ← パッケージマネージャの選択
Initialized empty Git repository in /path/to/study/nuxt/test_01/.git/
yarn install v1.12.3
info No lockfile found.
[1/4] 🔍  Resolving packages...
warning eslint > file-entry-cache > flat-cache > circular-json@0.3.3: CircularJSON is in maintenance only, flatted is its successor.
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
warning " > element-ui@2.4.11" has unmet peer dependency "vue@^2.5.2".
warning " > eslint-loader@2.1.1" has unmet peer dependency "webpack@>=2.0.0 <5.0.0".
warning " > eslint-plugin-vue@4.7.1" has incorrect peer dependency "eslint@^3.18.0 || ^4.0.0".
[4/4] 📃  Building fresh packages...
success Saved lockfile.
✨  Done in 180.40s.

  To get started:

    cd test_01
    yarn run dev

  To build & start for production:

    cd test_01
    yarn run build
    yarn start

インストールできたので実行してみます。

$ cd test_01
$ yarn run dev
yarn run v1.12.3
$ nuxt
ℹ Preparing project for development                                                                                                                                                                                                23:39:48
ℹ Initial build may take a while                                                                                                                                                                                                   23:39:48
✔ Builder initialized                                                                                                                                                                                                              23:39:48
✔ Nuxt files generated                                                                                                                                                                                                             23:39:48

✔ Client
  Compiled successfully in 6.89s

✔ Server
  Compiled successfully in 5.71s

ℹ Waiting for file changes                                                                                                                                                                                                         23:39:56

   ╭─────────────────────────────────────────────╮
   │                                             │
   │   Nuxt.js v2.3.4                            │
   │   Running in development mode (universal)   │
   │   Memory usage: 224 MB (RSS: 345 MB)        │
   │                                             │
   │   Listening on: http://localhost:3000       │
   │                                             │
   ╰─────────────────────────────────────────────╯

f:id:kojirooooocks:20181219030437p:plain

インストールできました。

2. ディレクトリ構造など

ディレクトリ構造は以下のとおりです。

$ tree -L 1
.
├── README.md
├── assets/
├── components/
├── layouts/
├── middleware/
├── node_modules/
├── nuxt.config.js
├── package.json
├── pages/
├── plugins/
├── static/
├── store/
└── yarn.lock

9 directories, 4 files

ディレクトリの用途は以下のとおりです。

  • assets

    • 画像やCSSなどを格納する
  • components

  • layouts

    • ページ全体のレイアウトテンプレートを格納する
    • ベーステンプレートという認識
  • middleware

  • pages

  • plugins

  • static

    • そのまま公開されてほしいリソースを格納する(favicon.icoなど)
  • store

    • Vuexストアを格納する

3. Routing

上記で記載したとおり、pagesはVue Routerの管轄内になります。

pages以下に、ディレクトリを切ってvueコンポーネントを配置していくと、それがそのままURLとなります。

つまり内部でVue Routerの設定などをよしなに作ってくれるということのようです。

例えば、pages以下のような感じでファイルなどを設置します。

$ tree pages/
pages/
├── README.md
├── index.vue
└── users
    ├── edit.vue
    └── index.vue

すると内部で自動的に以下のような感じのVue Routerの設定が作成されます。

routes: [{
  path: "/users",
  component: "pages/users/index.vue",
  name: "users"
}, {
  path: "/users/edit",
  component: "pages/users/edit.vue",
  name: "users-edit"
}, {
  path: "/",
  component: "pages/index.vue",
  name: "index"
}],

この設定は使用者は意識する必要がないので、この時点で実際にアクセスが可能です。

f:id:kojirooooocks:20181219030500p:plain

f:id:kojirooooocks:20181219030511p:plain

動的ルーティング

/users/:user_id のようにパラメータを使ったルーティングの定義も可能です。

動的ルーティングの場合は、_パラメータ名.vue というファイル名にする必要があります。

例えば今回の例の場合は、ファイル名は _user_id.vue としておく必要があります。

以下のような感じです。

$ tree pages/
pages/
├── README.md
├── index.vue
└── users
    ├── _user_id.vue
    ├── edit.vue
    └── index.vue

作成される設定は以下のような感じかと思います。

routes: [{
  path: "/users",
  component: "pages/users/index.vue",
  name: "users"
}, {
  path: "/users/edit",
  component: "pages/users/edit.vue",
  name: "users-edit"
}, {
  path: "/users/:user_id",
  component: "pages/users/_user_id.vue",
  name: "users-user_id"
}, {
  path: "/",
  component: "pages/index.vue",
  name: "index"
}],

これでアクセスが可能です。

f:id:kojirooooocks:20181219030530p:plain

ただ、現状 数字でも文字列でもなんでも _user_id.vue へ遷移してしまいます。

できれば数値のときのみに制限したいです。

こちらは公式サイトで乗っていたやり方のままですが、以下のようにroutingのバリデートを実行できます。

<template>
  <div>
    <p>
      users / user_id
    </p>
  </div>
</template>

<script>
export default {
  validate({ params }) {
    return /^\d+$/.test(params.user_id)
  }
}
</script>

f:id:kojirooooocks:20181219030545p:plain

user_idが数値以外の場合はルーティングが適用されてない事がわかります。

おまけ

こんな感じで動的なパラメータを使用してネストすることも出来ます。

また、渡されてきたパラメータを使用したい場合は以下のようにします。

$ tree pages/
pages/
├── README.md
├── index.vue
└── users
    ├── _party
    │   └── _user_id.vue
    ├── edit.vue
    └── index.vue
<template>
  <div>
    <p>
      {{ partyName }} / {{ userId }}
    </p>
  </div>
</template>

<script>
export default {
  validate({ params }) {
    if (!/^[A-Za-z]+$/.test(params.party)) {
      return false
    }
    return /^\d+$/.test(params.user_id)
  },
  computed: {
    userId: function() {
      return this.$route.params.user_id
    },
    partyName: function() {
      return this.$route.params.party
    }
  }
}
</script>

f:id:kojirooooocks:20181219030559p:plain

↑のようになります。

4. axiosを使用する

本書ではQiita APIを使用して、Qiitaから情報を取得する手順が丁寧に記載されています。

この方法を真似て、openweathermapから天気予報データを取得してみようと思います。

今回はインストール時にaxiosを使用するように設定しているので、nuxt.config.jsにはすでにaxiosの記述がmoduleの箇所に記載されています。

openweathermapから発行されるAPI keyを .envrc に記述して、 envを読み込むようにします。

ただし、openweathermapのAPIはQueryStringで送る形式のようなので、本書で紹介されている、axiosプラグインを作成してonRequestでリクエスト前にヘッダーを追加するといった機能は不必要なので省略しています。

.envrc
export OPENWEATHERMAP_API_KEY=XXXXXXXXX
nuxt.config.js
  env: {
    OPENWEATHERMAP_API_KEY: process.env.OPENWEATHERMAP_API_KEY
  }
pages/weather/index.vue
<template>
  <div>
    <p>只今の埼玉の天気</p>
    <span>{{ weather }}です</span>
  </div>
</template>

<script>
export default {
  async asyncData({ app }) {
    const weatherData = await app.$axios.$get(
      'https://api.openweathermap.org/data/2.5/weather?q=saitama,jp&APPID=' +
        process.env.OPENWEATHERMAP_API_KEY
    )

    return {
      weatherData: weatherData.weather[0].main
    }
  },

  computed: {
    weather: function() {
      if (this.weatherData === 'Clouds') {
        return '曇り'
      } else if (this.weatherData === 'Rain') {
        return '雨'
      } else if (this.weatherData === 'Snow') {
        return '雪'
      } else if (this.weatherData === 'Clear') {
        return '晴れ'
      } else {
        return this.weatherData
      }
    }
  }
}
</script>

f:id:kojirooooocks:20181219030633p:plain

表示されました。

今回使用したasyncDataは、nuxtが用意したvueのdataの拡張で、asyncDataの結果はdataにマージされます。

asyncDataはコンポーネントがロードされる前に実行されるもの(初期化前に実行されるもの)なので、aysncData内ではthisへのアクセスが出来ません。

asyncDataの第一引数に contextというデータが渡されるので、その中のappというルートのvueインスタンスからaxiosを呼び出すことが出来ます。

5. ページ遷移

先程作った天気予報に埼玉以外の天気を表示できるようにしてみます。

pages/weather/index.vue
<template>
  <div>
    <ul>
      <li><nuxt-link :to="`/weather/aomori`">青森の天気</nuxt-link></li>
      <li><nuxt-link :to="`/weather/saitama`">埼玉の天気</nuxt-link></li>
      <li><nuxt-link :to="`/weather/osaka`">大阪の天気</nuxt-link></li>
      <li><nuxt-link :to="`/weather/hiroshima`">広島の天気</nuxt-link></li>
      <li><nuxt-link :to="`/weather/okinawa`">沖縄の天気</nuxt-link></li>
    </ul>
  </div>
</template>
pages/weather/_pref.vue
<template>
  <div>
    <p>只今の{{ prefName }}の天気</p>
    <span>{{ weather }}です</span>
  </div>
</template>

<script>
export default {
  validate({ params }) {
    const availablePrefs = [
      'aomori',
      'saitama',
      'osaka',
      'hiroshima',
      'okinawa'
    ]
    return availablePrefs.indexOf(params.pref) !== -1
  },

  async asyncData({ app, params }) {
    const url = `https://api.openweathermap.org/data/2.5/weather?q=${
      params.pref
    }&APPID=${process.env.OPENWEATHERMAP_API_KEY}`
    const weatherData = await app.$axios.$get(url)

    return {
      weatherData: weatherData.weather[0].main
    }
  },

  computed: {
    weather: function() {
      if (this.weatherData === 'Clouds') {
        return '曇り'
      } else if (this.weatherData === 'Rain') {
        return '雨'
      } else if (this.weatherData === 'Snow') {
        return '雪'
      } else if (this.weatherData === 'Clear') {
        return '晴れ'
      } else {
        return this.weatherData
      }
    },

    prefName: function() {
      if (this.$route.params.pref === 'aomori') {
        return '青森'
      } else if (this.$route.params.pref === 'saitama') {
        return '埼玉'
      } else if (this.$route.params.pref === 'osaka') {
        return '大阪'
      } else if (this.$route.params.pref === 'hiroshima') {
        return '広島'
      } else {
        return '沖縄'
      }
    }
  }
}
</script>

f:id:kojirooooocks:20181219030743g:plain

いい感じで遷移しています。

遷移には、 <nuxt-link> を使用しています。

また、ほかの変更点としては、axiosでリクエストを飛ばす際に、urlパラメータを使用しているところくらいです。

6. メタの変更

タイトルやHTMLメタの修正の方法も本書に記載されています。

大本のタイトルの設定は nuxt.config.jsから行います。

  head: {
    title: pkg.name,
    titleTemplate: '%s | Kojirock Study Nuxt.js', ←追加

f:id:kojirooooocks:20181219030833p:plain

titleが変わりました。

ページごとにタイトルも設定できます。

pages/weather/index.vue
<template>
  <div>
    <ul>
      <li><nuxt-link :to="`/weather/aomori`">青森の天気</nuxt-link></li>
      <li><nuxt-link :to="`/weather/saitama`">埼玉の天気</nuxt-link></li>
      <li><nuxt-link :to="`/weather/osaka`">大阪の天気</nuxt-link></li>
      <li><nuxt-link :to="`/weather/hiroshima`">広島の天気</nuxt-link></li>
      <li><nuxt-link :to="`/weather/okinawa`">沖縄の天気</nuxt-link></li>
    </ul>
  </div>
</template>

<script>
export default {
  head() {
    return {
      title: '天気予報'
    }
  }
}
</script>

f:id:kojirooooocks:20181219030846p:plain

変更されました。

以下のように、ページごとにtitleTemplateも書き換えることも出来ます。

export default {
  head() {
    return {
      title: '天気予報',
      titleTemplate: '%s|AAAAA'
    }
  }
}

f:id:kojirooooocks:20181219030900p:plain

終わりに

まだ最初ですが、vueの本を事前にやっていたおかげで、スムーズに入っていけている感があります。

次はvuexのところらへんからやっていきますー。