BETA

VueJSでTodoアプリもどきを作ったときのつまりどころ

投稿日:2020-05-04
最終更新:2020-05-04

作ったもの

イメージ

Github

https://github.com/popy1017/vue-todo

前提

  • vue-cliでプロジェクトを作成 @vue/cli 4.3.1
  • データのローカルストレージ保存などは未実装
  • vue以外でつまづいたところも記載
  • 良いTodoアプリを作ることよりも、Vueの使い方の確認が目的

詰まりどころ

要素を横に並べる

Gridレイアウトを用いた。参考記事: CSS Grid Layout を極める!(基礎編) - Qiita

See the Pen css-grid-layout by popy1017 (@popy1017) on CodePen.

下記方法でもできたが、横幅が狭くなったときに縦並びになってしまった。
横に並べたい両方の要素にdisplay: inline-blockvertical-align: middleを指定する。

See the Pen css-inline-block by popy1017 (@popy1017) on CodePen.

参考記事: よく使うCSSで要素を横並びにする方法と使い分け - Qiita

日付型を簡単に扱うための dayjs をVueで使う

日付型を簡単に扱うためのライブラリと言えばMoment.jsが有名だが、いろいろ試してもModule not found: Error: Can't resolve './locale'という警告が消えなかったのでdayjsを使うことにした。(警告は出るが正常に動くとは思う。)

Vueでdayjsを使う

main.jsでグローバルなフィルターを定義したかったので、main.jsでインポートする。
フィルターだけで十分なら必要ないが、多くのコンポーネントでdayjsを利用したい場合は、各コンポーネントで個別にimportするのではなく、インスタンスプロパティに追加することで、他のコンポーネントから利用できるようになる。
インスタンスプロパティの追加 — Vue.js

View側から呼び出す際は$dayjs()、computedプロパティやmethodsプロパティから呼び出す際はthis.$dayjs()で呼び出せる。(filterプロパティ内ではthisが使えないため呼び出せないことに注意。)

/* main.js */  
import Vue from "vue";  
import App from "./App.vue";  

import "dayjs/locale/ja"; // ローカライズのために必要  
import dayjs from "dayjs"; // dayjs本体  
import relativeTime from "dayjs/plugin/relativeTime"; // 相対時間表示プラグイン  
import localizedFormat from "dayjs/plugin/localizedFormat"; // ローカライズフォーマットプラグイン  

dayjs.extend(relativeTime); // プラグインを導入  
dayjs.extend(localizedFormat); // プラグインを導入  
dayjs.locale("ja"); // ローカライズ  

Vue.config.productionTip = false;  
Vue.prototype.$dayjs = dayjs; // インスタンスプロパティに追加  

Vue.filter("fromNow", (date) => dayjs().to(date));  
Vue.filter("llll", (date) => dayjs(date).format("llll"));  

new Vue({  
  render: (h) => h(App),  
}).$mount("#app");  
<!-- App.vue -->  
<template>  
  <div id="app">  
    <h3>直接的な呼び出し</h3>  
    <p>{{$dayjs()}}</p>  
    <p>{{$dayjs().format("llll")}}</p>  
    <p>{{$dayjs().to(Date())}}</p>  
    <hr />  
    <h3>computed経由での呼び出し</h3>  
    <p>{{now}}</p>  
    <p>{{llll}}</p>  
    <p>{{fromNow}}</p>  
    <hr />  
    <h3>フィルター適用</h3>  
    <p>{{$dayjs() | fromNow}}</p>  
    <p>{{$dayjs() | llll}}</p>  
  </div>  
</template>  

<script>  
export default {  
  name: "App",  
  computed: {  
    now() {  
      return this.$dayjs();  
    },  
    fromNow(date) {  
      return this.$dayjs().to(date);  
    },  
    llll() {  
      return this.$dayjs().format("llll");  
    }  
  },  
  created() {  
    console.log(this);  
  }  
};  
</script>  

(念のため) momentの使い方

使い方はdayjsとほぼ一緒。

/* main.js */  
import Vue from "vue";  
import App from "./App.vue";  
import "moment/src/locale/ja";  
import moment from "moment";  

Vue.config.productionTip = false;  

Vue.prototype.$moment = moment;  
Vue.filter("LLLL", (date) => moment(date).format("LLLL"));  
Vue.filter("fromNow", (date) => moment(date).fromNow());  

new Vue({  
  render: (h) => h(App),  
}).$mount("#app");  
<!-- App.vue -->  
<template>  
  <div id="app">  
    <h3>直接的な呼び出し</h3>  
    <p>{{$moment()}}</p>  
    <p>{{$moment().format("llll")}}</p>  
    <p>{{$moment().fromNow()}}</p>  
    <hr />  
    <h3>computed経由での呼び出し</h3>  
    <p>{{now}}</p>  
    <p>{{llll}}</p>  
    <p>{{fromNow}}</p>  
    <hr />  
    <h3>フィルター適用</h3>  
    <p>{{$moment() | fromNow}}</p>  
    <p>{{$moment() | LLLL}}</p>  
  </div>  
</template>  

<script>  
export default {  
  name: "App",  
  computed: {  
    now() {  
      return this.$moment();  
    },  
    fromNow() {  
      return this.$moment().fromNow();  
    },  
    llll() {  
      return this.$moment().format("llll");  
    }  
  }  
};  
</script>  

$router.push時にparamsでオブジェクトを渡す

Routerのオプションで指定するpropsには関数を指定できるのでそれを使う。
ただし、画面遷移時($router.push実行時)にpathを指定するとうまく受け取れなかった(route.paramsに何も入らない)ので、nameを使う。
また遷移先のコンポーネントではpropsの設定が必要なので注意。

// routes設定の例  
routes: [  
    { name: "users",   
      path: "users/",  
      props: function(route) {  
        return { ...route.params };  
      }  
];  

// 画面遷移  
this.$router.push({  
    name: "users",  
    params: {  
        user: { id: 123, name: "Taro" }  
    }  
});  

サンプル

See the Pen vue-router-name-path by popy1017 (@popy1017) on CodePen.

keep-aliveで特定のコンポーネントのみキャッシュさせる

include属性でキャッシュさせたいコンポーネント名を指定する。exclude属性もある。
コンポーネントには、nameプロパティで名前をつけておく必要がある。
詳しくは以下のページ参照。
【Vue.js】インスタンスキャッシュ(keep-alive)の使用方法 - freamap ブログ

See the Pen keep-alive-include by popy1017 (@popy1017) on CodePen.

v-forとv-ifは一緒に使わない

詳しくは以下を参考。パフォーマンスが悪くなる、バグの温床に成りうるらしい。
条件にあったものだけリスト表示したい際は、computedプロパティを使うと良い。
スタイルガイド — Vue.js

所感

  • 今回はVueの基本を抑えるため、UdemyのVue講座受講の集大成として、Todoアプリを作成してみた。
  • cssの書き方や、日付型の持ち方など、あまり本質ではないところで時間がかかってしまった。
  • 直接cssをいじると、デザインフレームワークのありがたさが身に染みてわかる。
  • 実際にアプリを作ってみると、あれ?これどうやって使うんだっけ?となることが多いので、基礎も大事だが、実際にそれっぽいものを作ろうとしないと定着しない気がする。
  • コンポーネントの分割単位や、アプリ全体で使いたいデータの扱い方(Vuexは結局使った方が良いのかどうか)など、まだまだ理解できていないことが多い。
  • Todoアプリとしては、Todoの並び替えとか、期限までの時間で色分けとか、できることは多そう。
技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
駆け出しエンジニアからエキスパートまで全ての方々のアウトプットを歓迎しております!
or 外部アカウントで 登録 / ログイン する
クランチについてもっと詳しく

この記事が掲載されているブログ

@popy1017の技術ブログ

よく一緒に読まれる記事

0件のコメント

ブログ開設 or ログイン してコメントを送ってみよう