Nuxt.jsとTypeScriptを使ってGitHubの情報を閲覧できるツールを作ってみました。

Table of Contents

はじめに

経緯

Vue.jsを使ってSPAを作成する予定があるため、その勉強とBoilerplate作成を兼ねてとなります。
そのためツールを作り込むことは目的ではありません。

前提条件

以下の環境を想定して進めます。

  • npm5
  • Ubuntu16.04
    • Lubuntu
    • VMでありホストはWindows

ソースコード

以下で公開しています。

ツールのデモ

以下からお試しできます。

利用技術

以下の技術を利用しています。

  • Vue.js
  • Nuxt.js (1.3.x)
  • TypeScript (2.7.x)
  • Element (2.4.x)

Vue.js

Vue.jsはJavaScriptのフレームワークです。

なぜvue.js?

ReactとAngularは開発しているプロダクトがありますが、Vue.jsだけ開発経験が無いからです。
また2018/07/09現在において、ReactやAngularに比べ、以下の理由から職場でも導入しやすいと思っています。

  • Vue.jsはコスパが良い(学習コストとできることのバランス)
  • Vue.jsは日本語ドキュメントが充実している
  • Elementで必要なことがほとんどできる

ReactとAngularの経験も手伝ってはいますが、Vue.jsの習得コストは他の半分以下だと思います。

Nuxt.js

Nuxt.jsはVue.jsアプリケーションを作成するためのフレームワークです。

サーバサイドレンダリング(SSR)するためのフレームワークNext.jsに影響されていますが、今回はSPAとして作成するためSSRは利用しません。

なぜNuxt.js?

アプリケーションを開発する仕組みが整理されており、開発が楽だからです。
SSRを利用しなくても以下の機能を持っているため恩恵は十分です。

TypeScript

TypeScript はMicrosoftが開発しているJavaScriptのスーパーセットです。
強力な型システムを備え、中・大規模システムの開発には欠かせません。

なぜTypeScript?

安全で高速な開発を持続するためです。
初めて動かすまでの速度以外の面でTypeScriptを選ばない理由はないでしょう。

フレームワークやライブラリのチュートリアルはJavaScriptを対象にしていることが多いため、その点でTypeScriptが足を引っ張ることはありますが以前に比べて随分マシになりました。
少しの技術力とドキュメントを読む力があれば、そのデメリットより使用するメリットが遥かに上回るでしょう。

Element

ElementはVueのコンポーネント用UIライブラリです。

なぜElement?

見た目が格好良く、ツール作成に必要なUIがほぼ揃っているからです。
また、新しいComponentを使う時もimportなどが不要で非常に使いやすいです。

開発環境

IntelliJ IDEAでプラグインを使用して開発しています。

VSCodeだとVeturになりますが、Nuxt.jsの補完やジャンプが不十分でした。

アプリケーションを立ち上げる

まずはアプリケーションを立ち上げることを目指しました。

インストール

vue-cliをインストールします。

$ npm install -g vue-cli

templateからプロジェクトを作成

TypeScript用のNuxt.jsテンプレートを利用します。

$ vue init nuxt-community/typescript-template

依存モジュールをインストールします。

& npm i

package.jsonのscripts.devコマンドに以下オプションを追加します。

  "scripts": {
    "dev": "nuxt -H 0.0.0.0 --spa",

--spaはSPAとして起動するオプションです。(SSRを利用しない)

-H 0.0.0.0はlocalhost以外からのアクセスも許容するオプションです。
これは私がVM上のLubuntuで開発をしており、そこにホストOSのブラウザからアクセスする必要があるためです。

起動

以下で開発サーバが立ち上がります。

$ npm run dev

http://localhost:3000にアクセスすると画面が表示されます。

Elementの導入

UIとしてElementを導入します。
公式と以下の記事を参考にさせていただきました。

インストール

npmでインストールします。

$ npm i element-ui

使い方

Nuxt.jsにプラグインとして登録します。

pluginsディレクトリ配下にelement-ui.jsを作成します。

import Vue from 'vue'
import ElementUI from 'element-ui'
import locale from 'element-ui/lib/locale/lang/ja'

Vue.use(ElementUI, { locale })

nuxt.config.jsにいくつか設定を追加します。

  • plugins配下に{ src: '~plugins/element-ui' }を追加
  • css配下に"element-ui/lib/theme-chalk/index.css"を追加
  • build.vendor配下に"element-ui"を追加

後は公式ドキュメントの通りにvueファイルのtemplateを記述していけばOKです。

class-transformerの導入

class-transformerはObjectとクラスを相互変換可能なTypeScriptのライブラリです。
GitHub APIから取得した結果をクラスに変換するために利用しています。

使い方

例えば以下の様なinterfaceとclassが定義されているとします。

export interface Owner {
  id: number;
  login: string;
  avatar_url: string;
}

export class Repository {
  id: number;
  owner: Owner;
  license: {
    spdx_id: string;
  };

  get licenseName(): string {
    return this.license && this.license.spdx_id ? this.license.spdx_id : 'Unknown';
  }
}

以下のように書くとres: Objectresponse: Responseに変換できます。

const res: Object  = (await axios.get(URL)).data;
const response: Response = plainToClass(Response, res);

クラスに変換されたため、response.licenseNameのように呼び出すことができます。

Uncaught TypeError: Reflect.getMetadata is not a functionエラーが表示される場合..
import 'reflect-metadata'が記載されていることを確認して下さい。

ネストオブジェクトを変換したいとき

以下のようにRespositoryクラスのownerプロパティがクラスである場合を考えます。

export class Owner {
  id: number;
  login: string;
  avatar_url: string;

  get loginName(): string {
    return this.login;
  }
}

このときplainToClass(Response, res)ではownerはクラスに変換されません。
つまりresponse.owner.loginNameはエラーとなります。

変換したいネストしているプロパティに@Typesアノテーションを付けることでこれを解決します。

export class Repository {
  id: number;
  @Type(() => Owner)
  owner: Owner;
  license: {
    spdx_id: string;
  };

  get licenseName(): string {
    return this.license && this.license.spdx_id ? this.license.spdx_id : 'Unknown';
  }
}

font-awesomeの導入

Elementのアイコンは種類が少ないのでfont-awesomeを導入します。

インストール

いくつかインストールが必要です。

$ npm i @fortawesome/fontawesome-svg-core @fortawesome/free-solid-svg-icons @fortawesome/vue-fontawesome
  • fontawesome-svg: SVGを使う
  • free-solid-svg-icons: Solidの無料版を使う
  • vue-fontawesome: Vueで使うために必要

使い方

Nuxt.jsにプラグインとして登録します。

pluginsディレクトリ配下にfont-awesome.jsを作成します。

import Vue from 'vue'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
  faCoffee,
  faUser,
} from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'

library.add(
  faCoffee,
  faUser,
);

Vue.component('font-awesome-icon', FontAwesomeIcon)

Vue.config.productionTip = false

上記の例ではcoffeeuserというアイコンのみを使用します。
他のアイコンを使う場合はimportlibrary.addにそれぞれ追加する必要があります。

※ しばらくこれに気づかずハマりました…

nuxt.config.jsplugins配下に{ src: '~plugins/font-awesome' }を追加します。

vueファイルのtemplateには<font-awesome-icon icon="calendar" size="sm"/>のように書きます。

@nuxtjs/font-awesomeという公式提供のmoduleがありますが使い方が分からずに断念しました…

GitHub Pagesへデプロイするときの注意

ドメイン直下ではないためnuxt.config.jsの変更が必要です。
リポジトリ名をルーターのベースおよびfaviconのベースとして追加します。

module.exports = {
  mode: 'spa',
  router: {
    base: '/github-viewer/',
  },
  head: {
    link: [
      {
        rel: "icon",
        type: "image/x-icon",
        href: "/github-viewer/favicon.ico",
      }
    ]
  },

この変更によってnpm run devでアクセスする先もhttp://localhost:3000/github-viewer/repositoriesに変わります。
GitHub Pagesに特化した設定となってしまいますが大きな支障は無いと思います。

総括

Nuxt.jsとTypeScript、Elementなどを使用してgithub-viewerを作成してみました。

思ったより良いモノができたので必要に応じてメンテしてみるかもしれません。