8
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Vue.js / Vue.js + TS / Nuxt.js / Nuxt.js + TSでポケモンのタイプ相性表を実装してみる

Last updated at Posted at 2019-10-14

JSフレームワークであるVue.jsと、そのVue.jsのフレームワークであるNuxt.jsのどちらを使うべきか?といった議論は時々見かけます。

が、結局は使う場面による、という結論だと思うので、Vue(vue-cli)と、Nuxtで同じアプリケーションを作って、「始め方」や「書き方」を整理しておきたいと思います。

2019/10/16
続編として、これらをサーバ上で動かすための「Vue.js / Nuxt.jsのアプリをnginxで動かす(サブディレクトリ対応)」を書きました。

前提

題材と完成品

ポケモンのタイプ相性表

私自身がポケモンシリーズ好きということで、18種類あるタイプの相性表(同タイプ同士もある総当たり戦)を題材としました。

  • ●) 効果バツグン(2倍)
  • ▲) 効果いまひとつ(0.5倍)
  • ×) 効果なし(0倍)

完成したアプリの見た目がこちら。(縦軸が攻撃側、横軸が防御側)
image.png

完成したアプリとリポジトリ

※リポジトリは執筆当時なので今後更新がある・・・かも。

Vue.js(vue-cli) ver.

Vue.js(vue-cli) + TypeScript ver.

Nuxt.js ver.

Nuxt.js + TypeScript ver.

Vue.jsであればvue-cliの初期状態からviewsに1ページ足しただけ、Nuxt.jspagesに1ページ足しただけ、という超簡易実装です。

使用したWeb APIについて

そもそもポケモンのタイプ相性表に変動する余地はないのですが、Vueでアプリを作るとなったらほぼ必須となるaxiosも使うべく、無理やりWeb APIを使うことにしました。

ポケモンのタイプ情報を返却するAPI

自作しました。(こちらはGithubには上げてません)

レスポンス

[
  {
    "id": 1,
    "name": "ノーマル",
    "short_name": "ノ",
    "color": "b1b1b1",
    "attack_relations": [
      {
        "attack_id": 1,
        "defense_id": 1,
        "effect_rate": 1.0
      },
      {
        "attack_id": 1,
        "defense_id": 2,
        "effect_rate": 1.0
      },
// 

環境構築と実装

ここから上に挙げた各アプリの環境構築と実装について書いていきます。なお、node.jsはインストール済み(npmが使える状態)という前提です。

Vue.js(vue-cli)

vue-cliインストール

グローバル(-g)にインストールします。

$ npm install -g @vue/cli

参考)Vue CLI Installation

初期設定

アプリ名はvue-cli-appで、BabelRouterにチェックを入れます。(Linter / Formatterは任意で、Vuexは今回使ってないのでなくてもOK)

$ vue create vue-cli-app

Vue CLI v3.11.0
? Please pick a preset: Manually select features
? Check the features needed for your project:
 ◉ Babel
 ◯ TypeScript
 ◯ Progressive Web App (PWA) Support
 ◉ Router
❯◉ Vuex
 ◯ CSS Pre-processors
 ◉ Linter / Formatter
 ◯ Unit Testing
 ◯ E2E Testing

ローカル起動確認

 $ cd vue-cli-app
 $ npm run serve

ここまではいわゆるHello World的な部分なので問題はないかと思います。

Axiosインストール

$ npm install axios
+ axios@0.19.0

実装(コーディング)

全部載せると長くなりそうなので、ポイントを抜粋していきます。

Type.vueのtemplate

基本は<table>タグを使い、APIから取得するtypesという連想配列をループしていくだけです。背景色のRGBもAPIのレスポンスに入れてます。

views/Type.vue
<template>
  <div>
    <table>
      <tr>
        <td class="none">&nbsp;</td>
        <th v-for="type in types" :key="type.id"
          :style="{ 'background-color' : '#' + type.color }">
          {{ type.short_name }}
        </th>
      </tr>
      <tr v-for="type in types" :key="type.id">
        <th :style="{ 'background-color' : '#' + type.color }">
          {{ type.short_name }}
        </th>
        <td v-for="(relation, idx) in type.attack_relations" :key="idx"
          :class="[
            relation.effect_rate == 2? 'red': '',
            relation.effect_rate == 0.5? 'blue': '',
            relation.effect_rate == 0? 'bold': ''
          ]"
        >
          {{ toSymbol(relation.effect_rate) }}
        </td>
      </tr>
    </table>
  </div>
</template>

Type.vueのscript

ライフサイクルフックであるcreatedでAPIを叩いて、レスポンスをtypesに入れています。(あれ、types: []でいいんだっけ...types: {}...?

views/Type.vue
<script>
import axios from 'axios'
export default {
  name: 'type',
  data: function() {
    return {
      types: []
    }
  },
  created: function () {
    axios.get(process.env.VUE_APP_DOMAIN + '/v1/types')
      .then((response) => {
        this.types = response.data;
      })
  },
  methods: {
    toSymbol(rate) {
      switch(rate) {
        case 2:   rate = "";
                  break;
        case 0.5: rate = "";
                  break;
        case 0:   rate = "×";
                  break;
        default:  rate = "";
      }
      return rate;
    }
  },
}
</script>

ルーティング

追加した箇所だけの抜粋ですが、非常にシンプルです。

router.js
{
  path: '/type',
  name: 'type',
  component: Type
},

環境変数ファイル(.env)

vue-cli 3.0からはルートディレクトリに.envファイルを配置すると、process.env.hogehogeとして環境変数を利用できるようです。

ただし、変数の定義方法にルールがあり、VUE_APP_から始めること、です。

.env
VUE_APP_DOMAIN=http://localhost:8080

アプリ側からの参照方法は以下の通りです。

views/Type.vue
axios.get(process.env.VUE_APP_DOMAIN + '/v1/types')

環境別の環境変数

上記はAPIのドメインを設定していますが、ローカルでの開発時と、サーバでの運用時はAPIドメインが変わる、といった場合には以下のように設定が可能です。

.env.development
VUE_APP_DOMAIN=http://localhost:8080
.env.production
VUE_APP_DOMAIN=https://hogehoge.com

ローカルでnpm run serveした時は.env.development、サーバでnpm run build => npm run startした時は.env.productionを自動でを読みにいくようです。(やってないけど起動時に設定することも可能らしい)

Vue(vue-cli) + TypeScript

vue-cliインストール

※ Vue(vue-cli)と同じ

初期設定

アプリ名はvue-cli-ts-appで、BabelRouterと、今回はTypeScriptにもチェックを入れます。(Linter / Formatterは任意で、Vuexは今回使ってないのでなくてもOK)

$ vue create vue-cli-app

Vue CLI v3.11.0
? Please pick a preset: Manually select features
? Check the features needed for your project:
 ◉ Babel
 ◉ TypeScript
 ◯ Progressive Web App (PWA) Support
 ◉ Router
❯◉ Vuex
 ◯ CSS Pre-processors
 ◉ Linter / Formatter
 ◯ Unit Testing
 ◯ E2E Testing

ローカル起動確認

$ cd vue-cli-ts-app
$ npm run serve

Axiosインストール

※ Vue(vue-cli)と同じ

実装(コーディング)

構成などはVue.js(vue-cli)と変わらないので、メインはscript部分だけと言っても過言ではないです。

Type.vueのtemplate

※ Vue(vue-cli)と同じ

Type.vueのscript

ポイントはdatatypesに型定義が書かれていることと、created()toSymbol()が見た目変わらないこと、でしょうか。もちろん、createdはライフサイクルフックとして機能します。

views/Type.vue
<script lang="ts">
import axios from 'axios';
import { Component, Vue } from 'vue-property-decorator';
@Component({
  components: {
  },
})
export default class Type extends Vue {
  // data
  public types: Array<{
    id: number,
    name: string
    short_name: string,
    color: string,
    attack_relations: Array<{
      attack_id: number,
      defense_id: number,
      effect_rate: number;
    }>;
  }> = [];
  // created
  public created(): void {
    axios.get(process.env.VUE_APP_DOMAIN + '/v1/types')
      .then((response) => {
        this.types = response.data;
      });
  }
  // methods
  public toSymbol(rate: number) {
    let symbol: string;
    switch (rate) {
      case 2:   symbol = '';
                break;
      case 0.5: symbol = '';
                break;
      case 0:   symbol = '×';
                break;
      default:  symbol = '';
    }
    return symbol;
  }
}
</script>

ルーティング

※ Vue(vue-cli)と同じ

環境変数ファイル(.env)

※ Vue(vue-cli)と同じ

Nuxt.js

create-nuxt-app

npxというものが必要らしいのですが、npm 5.2.0からはデフォルトでバンドルされているそう。

参考)nuxt-js-を使ったスターターテンプレート

初期設定

アプリ名はnuxt-appで、package managerとしてNpmを選択し。Nuxt.js modulesAxiosを選択しておきます。

$ npx create-nuxt-app nuxt-app

create-nuxt-app v2.11.1
✨  Generating Nuxt.js project in nuxt-app
? Project name nuxt-app
? Project description My wicked Nuxt.js project
? Author name hogehoge
? Choose the package manager Npm
? Choose UI framework None
? Choose custom server framework None (Recommended)
? Choose Nuxt.js modules Axios
? Choose linting tools (Press <space> to select, <a> to toggle all, <i> to invert selection)
? Choose test framework None
? Choose rendering mode Universal (SSR)
? Choose development tools (Press <space> to select, <a> to toggle all, <i> to invert selection)

ローカル起動確認

vue-clinpm run serveだったので微妙に違う!

$ cd nuxt-app
$ npm run dev

Axiosインストール

create-nuxt-appでインストール済み

実装(コーディング)

type.vueのtemplate

※Vue(vue-cli) / Vue(vue-cli) + TSと同じ

type.vueのscript

ここもほとんどvue-cliと変わりませんが、APIのドメイン(環境変数)の部分が微妙に異なります。同じにもできるのですが、PREFIXなしでいけるのであえて変えています。(後述)

views/type.vue
<script>
import axios from 'axios'
export default {
  name: 'type',
  data: function() {
    return {
      types: []
    }
  },
  created: function () {
    axios.get(process.env.DOMAIN + '/v1/types')
      .then((response) => {
        this.types = response.data;
      })
  },
  methods: {
    toSymbol(rate) {
      switch(rate) {
        case 2:   rate = "";
                  break;
        case 0.5: rate = "";
                  break;
        case 0:   rate = "×";
                  break;
        default:  rate = "";
      }
      return rate;
    }
  },
}
</script>

ルーティング

Nuxt.jsにおけるルーティングは、pagesディレクトリの配下がそのままルーティングになるので、ファイルとしては不要です。

今回の場合、pages/type.vueなので、/typeというパスでアクセスすることになります。

環境変数ファイル(.env)

vue-cliでは、ルートディレクトリに.envを置くだけでしたが、Nuxt.jsはそれが使えないのでモジュールをインストールします。

dotenv

$ npm install @nuxtjs/dotenv

インストール後、モジュールを使うための設定と、環境別に読み込むための設定をnuxt.congfig.jsに入れます。

modules: [
  // Doc: https://axios.nuxtjs.org/usage
  '@nuxtjs/axios',
  [
    '@nuxtjs/dotenv',
    { filename: process.env.NODE_ENV !== 'production' ? ".env.dev" : ".env.prod" }
  ]
],

ちなみに、特に意味はありませんが、開発用を.env.dev、本番用を.env.proというファイル名にしました。(developmentproductionが長いと思ったので)

Nuxt.js + TypeScript

create-nuxt-app

※ Nuxt.jsと同じ

初期設定

アプリ名はnuxt-ts-appで、あとはNuxt.jsの時と同じです。
なお、vue-cliの時のように、TypeScriptをプリインストールすることはできません。

TypeScriptを使うための準備

ここがメインと言ってもいいでしょう。

インストール

$ npm install --save-dev @nuxt/typescript-build
+ @nuxt/typescript-build@0.2.9

設定

nuxt.config.js
buildModules: [
  '@nuxt/typescript-build'
],

以下は新規ファイルです。

tsconfig.json
{
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "moduleResolution": "node",
    "lib": [
      "esnext",
      "esnext.asynciterable",
      "dom"
    ],
    "esModuleInterop": true,
    "allowJs": true,
    "sourceMap": true,
    "strict": true,
    "noEmit": true,
    "baseUrl": ".",
    "paths": {
      "~/*": [
        "./*"
      ],
      "@/*": [
        "./*"
      ]
    },
    "types": [
      "@types/node",
      "@nuxt/types"
    ]
  },
  "exclude": [
    "node_modules"
  ]
}

参考) Nuxt TypeScript Setup

Class-basedで書きたい

上記の準備だけでTypeScript自体は使えるようですが、Class-basedで書くならいかが必要です。

$ npm install --save vue-property-decorator

先ほど作成したtsconfig.jsonに一行追記します。

tsconfig.json
"experimentalDecorators": true,

ローカル起動確認

※ Nuxt.jsと同じ

Axiosインストール

create-nuxt-appでインストール済み

実装(コーディング)

type.vueのtemplate

※ ※Vue(vue-cli) / Vue(vue-cli) + TS / Nuxt.jsと同じ

type.vueのscript

※ vue-cli + TypeScriptと同じ

ルーティング

※ Nuxt.jsと同じ

環境変数ファイル(.env)

※ Nuxt.jsと同じ


4つを並行して作っていたから少し混乱気味だったけど、こうして書いてみると「〜〜〜と同じ」が多いので、意外と異なる箇所はシンプルなのかな、と思いました。

ここから状態管理のVuexを使うと更に複雑になりそうですが、それはもう少し複雑なアプリになってから考えるとして、これくらいならNuxt.jsなしのVue.js単体でも十分そうです。

8
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?