jtk

【学習メモ】Vue.js入門 基礎から実践アプリケーション開発まで その6

7 Vuex によるデータフローの設計・状態管理

もスキップ。ざっくり読みましたが、データをどう持たせて処理するのが良いのかの参考になりました。

8 中規模・大規模向けのアプリケーション開発

  • ① 開発環境のセットアップ
  • ② 設計
  • ③ 実装

を実際に手を動かさず流し読みしましたが、設計というのはこういうことかと勉強に...
これまで WordPress 開発などでモジュール化して開発する際も、なんとなく全体は見てから開発を始めてその都度パーツ(コンポーネント)を作成してという形だったので、確かに前もってどの機能ごとに分けて命名決めて、処理すること明確にするべきだと感じました。
もう少し学習してからこのタスク管理アプリは写経できたらなと思います。

Appendix A jQuery からの移行

A.2 jQuery で実装していた機能の Vue.js による実装

A.2.1 イベントリスナー

jQuery

<div id="app">
  <button id="btn">Click</button>
</div>
// jQuery
$("#btn").on("click", function() {
  alert("Hi");
});

Vue.js

<div id="app">
  <button v-on:click="sayHi">Click</button>
  <!-- <button @click="sayHi">Click</button> でもOK -->
</div>
// Vue のマウント
var app = new Vue({
  el: "#app",
  methods: {
    sayHi: function(event) {
      // デフォルトで引数にイベントオブジェクトが渡される
      alert("Hi");
      console.log(event);
    }
  }
});

A.2.2 表示の切り替え

jQuery

<div id="app">
  <p id="on">オン</p>
  <p id="off">オフ</p>
  <button>ON/OFF切り替え</button>
</div>
// jQuery
const $on = $("#on");
const $off = $("#off");
let isOn = false;
$on.hide();
$("button").on("click", function() {
  isOn = !isOn;
  if (isOn) {
    $on.show();
    $off.hide();
  } else {
    $on.hide();
    $off.show();
  }
});

Vue.js

<div id="app">
  <p v-show="isOn">オン</p>
  <p v-show="!isOn">オフ</p>
  <button @click="toggle">ON/OFF切り替え</button>
</div>
var app = new Vue({
  el: "#app",
  data: function() {
    return {
      isOn: false
    };
  },
  methods: {
    toggle: function() {
      this.isOn = !this.isOn;
    }
  }
});

jQuery と Vue.js の違いは、イベントリスナー(イベントハンドラ)で明示的に表示の切り替え(DOM 操作)を行うか否かです。jQuery はイベントリスナーで jQuery オブジェクトを通して明示的に要素の表示を切り替えますが、Vue.js は事前にテンプレートでデータと要素の表示・非表示の関係を記述した上で、イベントハラではデータの更新を行うだけです。

A.2.3 要素の挿入・削除

jQuery

<div id="app">
  <p id="loading">ロードしています...</p>
</div>
// jQuery
const $loading = $("#loading");
const $app = $("#app");
setTimeout(function() {
  const $loaded = $("<p>ロードが完了しました</p>");
  $loading.remove();
  $app.append($loaded);
}, 1000);

Vue.js

<div id="app">
  <p v-if="isLoading">ロードしています...</p>
  <p v-else>ロードが完了しました</p>
</div>
var app = new Vue({
  el: "#app",
  data: function() {
    return {
      isLoading: true
    };
  },
  mounted: function() {
    setTimeout(() => {
      this.isLoading = false;
    }, 1000);
  }
});

A.2.4 属性値の変更

jQuery

<div id="app">
  <p><input type="text" disabled /></p>
  <button>入力欄を有効にする</button>
</div>
// jQuery
const $input = $("input");
const $button = $("button");
let disabled = true;
$button.on("click", function() {
  disabled = !disabled;
  $input.prop("disabled", disabled);
  if (disabled) {
    $button.text("入力欄を有効にする");
  } else {
    $button.text("入力欄を無効にする");
  }
});

Vue.js

<div id="app">
  <p><input type="text" :disabled="disabled" /></p>
  <button @click="toggleDisabled">{{ buttonText }}</button>
</div>
var app = new Vue({
  el: "#app",
  data: function() {
    return {
      disabled: true
    };
  },
  computed: {
    buttonText: function() {
      if (this.disabled) {
        return "入力欄を有効にする";
      } else {
        return "入力欄を無効にする";
      }
    }
  },
  methods: {
    toggleDisabled: function() {
      this.disabled = !this.disabled;
    }
  }
});

A.2.5 クラスの変更

jQuery

<style>
  .message {
    font-weight: bold;
  }
  .message.is-red {
    color: red;
  }
</style>
<div id="app">
  <p class="message">メッセージ</p>
  <button>文字色を切り替える</button>
</div>
// jQuery
const $message = $(".message");
$("button").on("click", function() {
  $message.toggleClass("is-red");
});

Vue.js

<style>
  .message {
    font-weight: bold;
  }
  .message.is-red {
    color: red;
  }
</style>
<div id="app">
  <p class="message" :class="{'is-red': isRed}">メッセージ</p>
  <button @click="toggleColor">文字色を切り替える</button>
</div>
var app = new Vue({
  el: "#app",
  data: function() {
    return {
      isRed: false
    };
  },
  methods: {
    toggleColor: function() {
      this.isRed = !this.isRed;
    }
  }
});

Vue.js(算出プロパティによるクラス付与)

<style>
  .message {
    font-weight: bold;
  }
  .message.is-red {
    color: red;
  }
</style>
<div id="app">
  <p class="message" :class="messageClasses">メッセージ</p>
  <button @click="toggleColor">文字色を切り替える</button>
</div>
var app = new Vue({
  el: "#app",
  data: function() {
    return {
      isRed: false
    };
  },
  computed: {
    messageClasses: function() {
      return {
        "is-red": this.isRed
      };
    }
  },
  methods: {
    toggleColor: function() {
      this.isRed = !this.isRed;
    }
  }
});

A.2.6 スタイルの変更

jQuery

<div id="app">
  <p class="message">メッセージ</p>
  <button data-color-name="red">赤色</button>
  <button data-color-name="yellow">黄色</button>
  <button data-color-name="blue">青色</button>
</div>
// jQuery
const $message = $(".message");
$("button").on("click", function(event) {
  $message.css("color", $(event.currentTarget).attr("data-color-name"));
});

Vue.js

<div id="app">
  <p class="message" :style="{color: color}">メッセージ</p>
  <button @click="changeColor('red')">赤色</button>
  <button @click="changeColor('yellow')">黄色</button>
  <button @click="changeColor('blue')">青色</button>
</div>
var app = new Vue({
  el: "#app",
  data: function() {
    return {
      color: ""
    };
  },
  methods: {
    changeColor: function(color) {
      this.color = color;
    }
  }
});

A.2.6 スタイルの変更

入力された値を取得する。送信時に入力内容を確認するダイアログを表示して、

  • 「OK」が押されればそのまま送信
  • 「キャンセル」が押されれば中止

jQuery

<form id="app" method="post" action="/questionaire">
  <p>
    <label for="name">名前</label>
    <input type="text" name="name" id="name" value="" />
  </p>
  <p>
    <label for="age">年齢</label>
    <input type="number" name="age" id="age" value="" />
  </p>
  <p>
    <input type="submit" value="送信" />
  </p>
</form>
// jQuery
const $nameInput = $("#name");
const $ageInput = $("#age");
const $form = $("form");
$form.on("submit", function(e) {
  let message = [
    "名前: " + $nameInput.val(),
    "年齢: " + $ageInput.val(),
    "この内容で送信しますか?"
  ].join("\n");
  if (!window.confirm(message)) {
    e.preventDefault();
  }
});

Vue.js

<form id="app" @submit="confirm" method="post" action="/questionaire">
  <p>
    <label for="name">名前</label>
    <input type="text" name="name" id="name" v-model="name" />
  </p>
  <p>
    <label for="age">年齢</label>
    <input type="number" name="age" id="age" v-model.number="age" />
  </p>
  <p>
    <input type="submit" value="送信" />
  </p>
</form>
var app = new Vue({
  el: "#app",
  data: function() {
    return {
      name: null,
      age: null
    };
  },
  methods: {
    confirm: function(e) {
      let message = [
        "名前: " + this.name,
        "年齢: " + this.age,
        "この内容で送信しますか?"
      ].join("\n");
      if (!window.confirm(message)) {
        e.preventDefault();
      }
    }
  }
});
v-model の振る舞い

number 修飾子

入力を数値に変換します。

lazy 修飾子

lazy ディレクティブは、同期のタイミングを指定します。
同期のタイミングを change イベントが発火したタイミングに変更できます。
(入力が完了して、input要素のフォーカスが外れたタイミング)