Vue用UIコンポーネントライブラリのVuetifyを使ってみた

Vue用UIコンポーネントライブラリのVuetifyを使ってみた

Vue.js

公開: 2019-09-16

更新: 2019-09-16

こんにちは、お読みいただきありがとうございます。 ケン(@gootablog)です。

仕事で管理画面を作る必要があり、初めてVuetifyを使いました。備忘録してどういうものを使ったか、UIコンポーネントを使ってみてどうだったかなどを書いていきます。

version

@vue/cli@3.9.3

{
  "name": "vuetify-sample",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint"
  },
  "dependencies": {
    "core-js": "^2.6.5",
    "vue": "^2.6.10",
    "vue-router": "^3.0.3",
    "vuetify": "^2.0.14"
  },
  "devDependencies": {
    "@vue/cli-plugin-babel": "^3.11.0",
    "@vue/cli-plugin-eslint": "^3.11.0",
    "@vue/cli-service": "^3.11.0",
    "@vue/eslint-config-prettier": "^5.0.0",
    "babel-eslint": "^10.0.1",
    "eslint": "^5.16.0",
    "eslint-plugin-prettier": "^3.1.0",
    "eslint-plugin-vue": "^5.0.0",
    "prettier": "^1.18.2",
    "vue-template-compiler": "^2.6.10"
  }
}

vuetifyとは

Vue Material Design Component Framework — Vuetify.js

  • UIコンポーネント
  • きれい
  • ドキュメントが日本語翻訳されてる
  • 201907にv2がリリースされた

vuetify install

基本的にここを見て自分にあった方法を行えば大丈夫

自分はvue-cliで追加しました

vue add vuetify

プラグイン用のファイル作成

vue add vuetifyでインストールすると自動で設定をしてくれますが一応書きます。

ここに設定を書く。
今回は自動で必要なものを読み込む設定にする。 それプラスiconの読み込み

// src/plugins/vuetify.js

import Vue from "vue";
import Vuetify from "vuetify/lib";

Vue.use(Vuetify);

export default new Vuetify({
  icons: {
    iconfont: "mdi"
  }
});

main.jsに書き込んで使えるようにする

設定を書いたファイルを読み込む

//...

import vuetify from "@/plugins/vuetify";

// ...

new Vue({
  vuetify,
  router,
  render: h => h(App)
}).$mount("#app");

使用例

実際に使ったUIコンポーネントのサンプルをおいておきます。

サイト
https://vue-sample.kens-portfolio.com/vuetify

ソースコード
https://github.com/Kenj-I/vueSample

vuetifyを使うときはApp.vueで大枠をv-appタグで囲んでから使用しましょう。

テーブル

https://vue-sample.kens-portfolio.com/vuetify/table

<template>
  <div>
    <v-data-table
      :headers="headers"
      :items="listData"
      :items-per-page="2"
      :sort-by="'name'"
      :sort-desc="true"
      :footer-props="footerProps"
    >
      <template v-slot:item.column4="{ item }">
        <span v-if="item.column4 === '444'">○</span>
        <span v-else>X</span>
      </template>
    </v-data-table>
  </div>
</template>

<script>
export default {
  data: () => ({
    loading: true,
    headers: [
      { text: "Name", value: "name" },
      { text: "Column1", value: "column1" },
      { text: "Column2", value: "column2" },
      { text: "Column3", value: "column3" },
      { text: "Column4", value: "column4" }
    ],
    footerProps: {
      "items-per-page-options": [2, 4, 6]
    },
    listData: [
      {
        name: "AAA",
        column1: "111",
        column2: "222",
        column3: "333",
        column4: "444"
      },
      {
        name: "BBB",
        column1: "111",
        column2: "222",
        column3: "333",
        column4: "444"
      },
      {
        name: "CCC",
        column1: "111",
        column2: "222",
        column3: "333",
        column4: "555"
      },
      {
        name: "DDD",
        column1: "111",
        column2: "222",
        column3: "333",
        column4: "555"
      },
      {
        name: "EEE",
        column1: "111",
        column2: "222",
        column3: "333",
        column4: "444"
      },
      {
        name: "FFF",
        column1: "111",
        column2: "222",
        column3: "333",
        column4: "444"
      },
      {
        name: "GGG",
        column1: "111",
        column2: "222",
        column3: "333",
        column4: "444"
      },
      {
        name: "HHH",
        column1: "111",
        column2: "222",
        column3: "333",
        column4: "444"
      },
      {
        name: "III",
        column1: "111",
        column2: "222",
        column3: "333",
        column4: "444"
      },
      {
        name: "JJJ",
        column1: "111",
        column2: "222",
        column3: "333",
        column4: "444"
      }
    ]
  })
};
</script>

<style lang="scss" scoped>
div {
  width: 800px;
  margin: 0 auto;
}
</style>

こんな感じでv-data-tableタグに設定やデータを渡してあげると簡単にテーブルを表示することができます。

値を加工したい場合はtemplateタグを使って加工する値のキーを指定してよしなにすることができます。

これだけでソートもできる、表示数も変えられる、ページネーションもできるって最高すぎます。

仕事ではサーバーサイドページネーションで作ったので表示数を変えたりソートしたりしたときは都度APIコールするようにして作成しました。

ダイアログ

https://vue-sample.kens-portfolio.com/vuetify/dialog

<template>
  <v-layout>
    <v-dialog v-model="dialog" :max-width="maxWidth">
      <template v-slot:activator="{ on }">
        <v-btn v-if="showBtn" class="btn" :color="btnColor" dark v-on="on">{{
          btnText
        }}</v-btn>
      </template>
      <v-card class="dialog">
        <v-card-title>
          <span class="dialog-title"> {{ dialogTitle }}</span>
        </v-card-title>
        <v-card-text>
          <span class="dialog-text">
            {{ dialogText }}
          </span>
        </v-card-text>
        <v-card-actions>
          <v-spacer></v-spacer>
          <v-btn class="btn" :color="cancelColor" @click="cancel">
            {{ cancelText }}
          </v-btn>
          <v-btn class="btn" :color="executeColor" @click="execute">
            {{ executeText }}
          </v-btn>
        </v-card-actions>
      </v-card>
    </v-dialog>
  </v-layout>
</template>

<script>
export default {
  props: {
    showBtn: {
      type: Boolean,
      default: true
    },
    btnText: {
      type: String
    },
    btnColor: {
      type: String,
      default: "#4BB9C0"
    },
    maxWidth: {
      type: Number,
      default: 500
    },
    cancelColor: {
      type: String,
      default: "#D53476"
    },
    executeColor: {
      type: String,
      default: "#4BB9C0"
    },
    cancelText: {
      type: String,
      default: "Disagree"
    },
    executeText: {
      type: String,
      default: "Agree"
    },
    dialogTitle: {
      type: String,
      required: true
    },
    dialogText: {
      type: String,
      default: ""
    }
  },
  data: () => ({
    dialog: false
  }),
  methods: {
    execute() {
      this.close();
      this.$emit("execute");
    },
    cancel() {
      this.close();
      this.$emit("cancel");
    },
    open() {
      this.dialog = true;
    },
    close() {
      this.dialog = false;
    }
  }
};
</script>

<style lang="scss" scoped>
.btn {
  display: block;
  color: white !important;
}
</style>

データを削除したり、更新するとき、ステータスを変えるときに使用しました。

ダイアログのレイアウトはいろいろと設定ができるのでサイズやカラーなどサイトに合うようにカスタマイズできます。

表示されるときは親コンポーネントでrefsを使って子コンポーネントのmethodsを実行して表示するようにしています。そしてダイアログで確認後の実行アクションはemitを使い親コンポーネントに知らせています。

アラート

https://vue-sample.kens-portfolio.com/vuetify/alert

<template>
  <transition>
    <div v-if="show" class="alert">
      <v-alert v-if="type" v-model="show" :type="type" outlined dense>{{
        message
      }}</v-alert>
    </div>
  </transition>
</template>

<script>
export default {
  props: {
    show: {
      type: Boolean,
      default: false
    },
    type: {
      type: String,
      default: ""
    },
    message: {
      type: String,
      default: ""
    }
  }
};
</script>

<style lang="scss" scoped>
.alert {
  box-shadow: 0px 0px 5px rgba(#000, 0.1);
  position: fixed;
  top: 10px;
  right: 5px;

  >>> .v-alert {
    margin: 0;
    height: 60px;
    display: flex;
    justify-content: center;
    align-items: center;
  }
}

.v-enter-active,
.v-leave-active {
  transition: all 0.5s;
}

.v-enter,
.v-leave-to {
  right: -100px;
  opacity: 0;
}
</style>

APIを実行したときなどの結果をアラートを使って表示させました。
右上から出てきて数秒後に消すという感じにしています。実際にはVuexで状態管理をしました。
現状複数のアラートを出すと書き換わってしまうので、複数出せるようにしたいところ。

ローディング

https://vue-sample.kens-portfolio.com/vuetify/loading

<template>
  <div v-if="loading" class="progress-bar">
    <v-progress-linear
      :indeterminate="true"
      :fixed="true"
      color="#5252D0"
    ></v-progress-linear>
    <div class="fullview" v-if="fullView"></div>
  </div>
</template>

<script>
export default {
  props: {
    loading: {
      type: Boolean,
      required: true
    },
    fullView: {
      type: Boolean,
      required: true
    }
  }
};
</script>

<style lang="scss" scoped>
.fullview {
  width: 100%;
  height: 100vh;
  background-color: #f5f7fc;
  position: fixed;
  top: 0;
  left: 0;
  z-index: 50;
  opacity: 0.5;
}
.progress-bar {
  width: 100%;
  height: auto;
  box-shadow: 0px 0px 5px rgba(#000, 0.1);
  position: fixed;
  top: 0;
  left: 0;
  z-index: 100;
}
</style>

ローディングはいくつかパターンがありましたが、プログレスバーを使用しました。非同期で送るのでしっかりリクエストを送ってるのはわかるようにしてます。

ローディングをしているときは、レイヤーを一枚挟んでクリックなどの操作をしないようにしています。

UIコンポーネントを使ってみて

大きめのUIコンポーネントを使ったのはこの4つくらいでした。これくらいで基本的な管理のページを作れるのはいいですね。レイアウト(flexやmarginなど)もタグに設定を書き足せばいいだけなのでとても便利でした。

ただこれまでBootstrapなどのCSSフレームワークを使ってこず、基本はフルスクラッチみたいな感じでやってきました。初めて使ってみて、使い方を理解するまでは少し難しさを感じましたが慣れるとサクサク書けます。ポイントポイントで使うととても強力なツールになりそうです。

今後も機会があったら積極的につかってみようと思います。