TypeScriptとクラススタイルで始めるVuetify生活

こんにちは。サービスグループの武田です。

Vueでマテリアルデザインをやりたいといった場合、いくつかの選択肢があります。その中のVuetifyをインストールしてみたところ、警告やエラーが発生して少し修正が必要でした。今回はエラーがなくなるよう修正する方法をお届けします。またクラススタイルで開発をしたかったのでその書き換えも行っています。

このエントリで使ったソースコードはGitHubに上げてあります。全体を見たい方はそちらも参照してください。

環境

今回の検証環境は以下となります。

$ node -v
v10.15.3

$ vue -V
3.5.1

$ sw_vers
ProductName:	Mac OS X
ProductVersion:	10.14.3
BuildVersion:	18D109

Vueのバージョンは2.6.10、Vuetifyは1.5.7です。また、vueコマンドがインストールされていない場合は、npm install -g @vue/cliでインストールします。

プロジェクトの作成

まずはVue CLIを利用してプロジェクトを作成します。

$ vue create getting-started-vuetify

presetはManualにして、すべてデフォルトを選択します。class-styleの項目がYesになっているのがポイントです。

最後の項目を選択するとインストールが始まります。インストールが終わったら一度起動してみましょう。

$ cd getting-started-vuetify
$ npm run serve -- --open

無事にWelcome画面が確認できました。

プロジェクトにVuetifyを追加

続いて先ほど作成したプロジェクトにVuetifyをインストールします。インストールする方法はいくつか用意されているため、詳細は公式のQuick Startを見てください。今回はvueコマンドを使って追加します。

Quick Start — Vuetify.js

$ vue add vuetify

途中でpresetを聞かれますが、推奨されているDefaultを選択します。

インストールが終わると変更のあったファイルが一覧表示されます。git diffなどで差分が確認できるので一度見ておくといいでしょう。

それではこの状態のまま一度起動してみましょう。

$ npm run serve -- --open

警告やらエラーやらが大量に出ますが、一応起動はできているようで、ブラウザでページを確認できました。

プロジェクトの警告とエラーを解消

起動できることはわかりましたが、大量の警告とエラーが出ているまま開発はできません。順に解消し、ついでに従来のスタイルで書かれているコンポーネントをクラススタイルに修正していきます。

修正は次の6ファイルです。

  • tsconfig.json
  • src/main.ts
  • src/plugins/vuetify.ts
  • src/App.vue
  • src/views/Home.vue
  • src/components/HelloWorld.vue

tsconfig.json

plugins/vuetify.tsで次のエラーが出ています。

ERROR in /private/tmp/getting-started-vuetify/src/plugins/vuetify.ts
2:21 Could not find a declaration file for module 'vuetify/lib'. '/private/tmp/getting-started-vuetify/node_modules/vuetify/lib/index.js' implicitly has an 'any' type.
  Try `npm install @types/vuetify` if it exists or add a new declaration (.d.ts) file containing `declare module 'vuetify/lib';`

要は使っているモジュールの型がわからん!とコンパイラが言っているわけですので、分かるようにしてあげます。次のようにtsconfig.jsoncompilerOptions.types[]vuetifyを追加するだけです。

diff --git a/tsconfig.json b/tsconfig.json
index e7e7290..34ba4bb 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -14,7 +14,8 @@
     "types": [
       "webpack-env",
       "mocha",
-      "chai"
+      "chai",
+      "vuetify"
     ],
     "paths": {
       "@/*": [

src/main.ts

単にLintの警告が出ているだけです。フォーマットをそろえてあげます。

diff --git a/src/main.ts b/src/main.ts
index 59151b0..37dd2b0 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -1,5 +1,5 @@
 import Vue from 'vue';
-import './plugins/vuetify'
+import './plugins/vuetify';
 import App from './App.vue';
 import router from './router';
 import store from './store';

src/plugins/vuetify.ts

こちらもLintの警告が出ているだけです。

diff --git a/src/plugins/vuetify.ts b/src/plugins/vuetify.ts
index 975696e..9198889 100644
--- a/src/plugins/vuetify.ts
+++ b/src/plugins/vuetify.ts
@@ -1,7 +1,7 @@
-import Vue from 'vue'
-import Vuetify from 'vuetify/lib'
-import 'vuetify/src/stylus/app.styl'
+import Vue from 'vue';
+import Vuetify from 'vuetify/lib';
+import 'vuetify/src/stylus/app.styl';

 Vue.use(Vuetify, {
   iconfont: 'md',
-})
+});

src/App.vue

App.vueでは次の修正を加えています。

  1. Vue Routerの復活
  2. HelloWorldコンポーネントのロード削除

もともと作成されたScaffoldでは、Vue Routerで画面切り替えを実装していました。vue addしたタイミングで上書きされてしまったため、それを復活させています *1。またHelloWorldコンポーネントをロードしている箇所は、もともとHomeビューでロードしているので、そちらに寄せるため該当コードは削除しています。

diff --git a/src/App.vue b/src/App.vue
index 37a339f..0abd6d7 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -5,7 +5,12 @@
         <span>Vuetify</span>
         <span class="font-weight-light">MATERIAL DESIGN</span>
       </v-toolbar-title>
-      <v-spacer></v-spacer>
+      <v-spacer/>
+      <v-toolbar-title class="headline">
+        <router-link to="/">Home</router-link> |
+        <router-link to="/about">About</router-link>
+      </v-toolbar-title>
+      <v-spacer/>
       <v-btn
         flat
         href="https://github.com/vuetifyjs/vuetify/releases/latest"
@@ -16,23 +21,7 @@
     </v-toolbar>

     <v-content>
-      <HelloWorld/>
+      <router-view/>
     </v-content>
   </v-app>
 </template>
-
-<script>
-import HelloWorld from './components/HelloWorld'
-
-export default {
-  name: 'App',
-  components: {
-    HelloWorld
-  },
-  data () {
-    return {
-      //
-    }
-  }
-}
-</script>

src/views/Home.vue

HelloWorldコンポーネントはmsgプロパティが必要なくなったため、単に呼び出すように修正します。

diff --git a/src/views/Home.vue b/src/views/Home.vue
index 2187e5c..01cedfe 100644
--- a/src/views/Home.vue
+++ b/src/views/Home.vue
@@ -1,7 +1,6 @@
 <template>
   <div class="home">
-    <img alt="Vue logo" src="../assets/logo.png">
-    <HelloWorld msg="Welcome to Your Vue.js + TypeScript App"/>
+    <HelloWorld/>
   </div>
 </template>

src/components/HelloWorld.vue

template部分はそのままですが、scriptは全面的に修正するためdiffではなくソースをそのまま掲載します。

// <template>は変更なし

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';

@Component
export default class HelloWorld extends Vue {
  private ecosystem = [
    {
      text: 'vuetify-loader',
      href: 'https://github.com/vuetifyjs/vuetify-loader',
    },
    {
      text: 'github',
      href: 'https://github.com/vuetifyjs/vuetify',
    },
    {
      text: 'awesome-vuetify',
      href: 'https://github.com/vuetifyjs/awesome-vuetify',
    },
  ];

  private importantLinks = [
    {
      text: 'Documentation',
      href: 'https://vuetifyjs.com',
    },
    {
      text: 'Chat',
      href: 'https://community.vuetifyjs.com',
    },
    {
      text: 'Made with Vuetify',
      href: 'https://madewithvuetifyjs.com',
    },
    {
      text: 'Twitter',
      href: 'https://twitter.com/vuetifyjs',
    },
    {
      text: 'Articles',
      href: 'https://medium.com/vuetify',
    },
  ];

  private whatsNext = [
    {
      text: 'Explore components',
      href: 'https://vuetifyjs.com/components/api-explorer',
    },
    {
      text: 'Select a layout',
      href: 'https://vuetifyjs.com/layout/pre-defined',
    },
    {
      text: 'Frequently Asked Questions',
      href: 'https://vuetifyjs.com/getting-started/frequently-asked-questions',
    },
  ];
}
</script>

以上で修正は完了です。

修正後の確認

修正できたら再びアプリケーションを起動して確認してみます。

$ npm run serve -- --open

問題なさそうです!

おまけ:テストケースの修正

以上でソースコードを修正して動作確認ができました。これで終わりでも問題はないのですが、テストケースが失敗して少し気持ち悪いので、パスするように直してみました。これについてはお好みでどうぞ。

diff --git a/tests/unit/example.spec.ts b/tests/unit/example.spec.ts
index 732a277..5a260ca 100644
--- a/tests/unit/example.spec.ts
+++ b/tests/unit/example.spec.ts
@@ -3,11 +3,9 @@ import { shallowMount } from '@vue/test-utils';
 import HelloWorld from '@/components/HelloWorld.vue';

 describe('HelloWorld.vue', () => {
-  it('renders props.msg when passed', () => {
-    const msg = 'new message';
-    const wrapper = shallowMount(HelloWorld, {
-      propsData: { msg },
-    });
+  it('renders welcome message', () => {
+    const msg = 'Welcome to Vuetify';
+    const wrapper = shallowMount(HelloWorld);
     expect(wrapper.text()).to.include(msg);
   });
 });
diff --git a/tests/e2e/specs/test.js b/tests/e2e/specs/test.js
index 479bea8..4a4e76f 100644
--- a/tests/e2e/specs/test.js
+++ b/tests/e2e/specs/test.js
@@ -3,6 +3,6 @@
 describe('My First Test', () => {
   it('Visits the app root url', () => {
     cy.visit('/')
-    cy.contains('h1', 'Welcome to Your Vue.js + TypeScript App')
+    cy.contains('h1', 'Welcome to Vuetify')
   })
 })

まとめ

Vueアプリケーションでマテリアルデザインを始めるにあたってVuetifyは有力な選択肢のひとつです。コマンド1行でインストールもでき簡単でした。ただインストール後に少し修正が必要だったので、これから始めようという方の助けになれば幸いです。

脚注

  1. リンクは簡略化のためToolbarに追加していますが、Navigation drawerなどを追加した方がきれいかと思われます