yuheijotaki.com

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

Vue.js入門 を読んでいます。
ネコ本、イヌ本を読んだので実践編と勝手に意気込んでいましたが、どうやら逆で概要や考え方的な内容からの記載が多く、より深く学べそうな気がします。

Vue.js入門 基礎から実践アプリケーション開発まで

Vue.js入門 基礎から実践アプリケーション開発まで

  • 作者: 川口和也,喜多啓介,野田陽平,手島拓也,片山真也
  • 出版社/メーカー: 技術評論社
  • 発売日: 2018/09/22
  • メディア: 単行本(ソフトカバー)
  • この商品を含むブログを見る

2章までですが、気になった箇所を引用をしながら思ったことをメモしておきます。

2 Vue.js の基本

2.1 Vue.js でUIを構築する際の考え方

2.1.2 Vue.js のUI構築

jQueryやDOM APIを利用したUIの構築では、DOMツリーやDOM要素がUIの状態を持ってしまうという問題がありました。これだとDOMツリー構造の変更が本来はDOMツリーやDOM要素と関係のないUIの状態を扱うロジックに影響を及ぼしてしまいます。仮にJavaScriptオブジェクトとしてUIの状態を持たせても、それをどのようにDOMツリーに反映するかまた別問題として出てきます。


Vue.jsでは、UIの状態をJavaScriptオブジェクトとして、DOMツリーやDOM要素とは完全に切り離した上で、全勝で説明のあったリアクティブな単方向のデータバインディングにより、UIの状態の変更に伴う要素の更新を自動で行うことで、この問題を解決しています。

ここではjQueryとの、UI構築やコーディングスタイルの比較が書かれています。 自分の頭のなかでは、「jQueryでできること」=「Vue.jsでもできる」という解釈だけでいてしまって、 尚且JavaScriptのライブラリ(フレームワーク)を扱うのが初めてだったので、構造的な視点でのVue.jsの特長を知るというところでは大変参考になりました。

2.11.1 イベントオブジェクト

算出プロパティ computed について

依存しているデータが変更されない限り、一度計算した結果をキャッシュする特徴をもっています。つまり、サンプルで用いた totalPrice の場合、一度計算をおこなった後は購入個数が変わるまで再計算をおこなわないということです。

メソッドプロパティ methods について

算出プロパティと似た機能としてVueインスタンスにはメソッドもあります。以下のように合計金額を計算するメソッドを定義してテンプレートで呼び出す({{ totalPrice() }})ことでも同様のことを実現できます。メソッドはキャッシュされません。メソッドが呼ばれる度に計算がされます。

コード

サンプルみながら書いたコードを貼っておきます。

<div id="app">
  <div id="app">
    <!-- `v-on` でイベントが発生したときに属性値で指定した式を評価する -->
    <ul>
      <li v-for="item in items" v-bind:key="item.name">
        {{ item.name }}の個数: <input type="number" v-on:input="item.quantity = $event.target.value" v-bind:value="item.quantity" min="0">
      </li>
    </ul>
    <hr>
    <!-- `v-on` ディレクティブの代わりに `v-model` を津使う -->
    <ul>
      <li v-for="item in items" v-bind:key="item.name">
        {{ item.name }}の個数: <input type="number" v-model="item.quantity" min="0">
      </li>
    </ul>
    <hr>
    <div v-bind:style="errorMessageStyle">
      <ul>
        <!-- 各商品の単価と購入個数をリスト表示する -->
        <li v-for="item in items" v-bind:key="item.name">
          {{ item.name }}: {{ item.price }} x {{ item.quantity }} = {{ item.price * item.quantity | numberWithDelimiter }} 円
        </li>
      </ul>
      <p>{{ items[0].name }} : {{ items[0].price }} x {{ items[0].quantity }}</p>
      <p>{{ items[0].name }} : {{ items[0].price * items[0].quantity }}</p>
      <p>フィルタ処理例 : {{ 1000 | numberWithDelimiter }}</p>
      <p>小計 : {{ totalPrice | numberWithDelimiter }}</p>
      <p>合計(税込み) : {{ totalPriceWithTax | numberWithDelimiter }}</p>
      <button v-bind:title="loggedInButton">購入</button>
      <button v-bind:disabled="!canBuy">購入</button>
      <p v-show="!canBuy">
        {{ 1000 | numberWithDelimiter }}円以上からご購入いただけます
      </p>
      <p v-bind:class="{shark: true, mecha: false}">テキスト</p>
      <!-- JavaScript の式を用いた場合 `{error: !canBuy}` -->
      <p v-bind:class="{error: !canBuy}">1000円以上からご購入いただけます</p>
      <!-- クラスで定義した場合 `.errorMessageClass` -->
      <p v-bind:class="errorMessageClass">1000円以上からご購入いただけます</p>
      <!-- ボタンが押されたら、メソッドを呼び出す -->
      <button v-bind:disabled="!canBuy" v-on:click="doBuy">購入</button>
    </div>
  </div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.22/dist/vue.js"></script>
<script src="./assets/js/main.js"></script>
// 商品の配列を作成
var items = [
  {
    name: '鉛筆',
    price: 300,
    quantity: 1
  },
  {
    name: 'ノート',
    price: 400,
    quantity: 1
  },
  {
    name: '消しゴム',
    price: 500,
    quantity: 0
  }
]

var vm = new Vue({
  el: '#app',
  data: {
    items: items,
    loggedInButton: 'ログイン済みのため購入できます。',
    // canBuy: false
  },
  filters: {
    // この説で追加したフィルタの処理
    numberWithDelimiter: function (value) {
      if (!value) {
        return '0'
      }
      return value.toString().replace(/(\d)(?=(\d{3})+$)/g, '$1,');
    }
  },
  methods: {
    doBuy: function () {
      // 本来はここで、サーバーと通信を行う
      alert(this.totalPriceWithTax + '円のお買い上げ!');
      this.items.forEach(function (item) {
        item.quantity = 0;
      });
    }
  },
  computed: {
    totalPrice: function () {
      // this経由でインスタンス内のデータにアクセス
      return this.items.reduce(function (sum, item) {
        return sum + (item.price * item.quantity);
      }, 0)
    },
    totalPriceWithTax: function () {
      // 算出プロパティに依存した算出プロパティも定義できる
      return Math.floor(this.totalPrice * 1.08);
    },
    canBuy: function () {
      return this.totalPrice >= 1000 // 1000円以上から購入可能にする
    },
    errorMessageClass: function () {
      // canBuy が偽のときに .error クラスを付与する
      return {
        error: !this.canBuy
      }
    },
    errorMessageStyle: function () {
      // canBuy が偽のときに赤く表示する
      return {
        border: this.canBuy ? '' : '1px solid red',
        color: this.canBuy ? '' : 'red',
      }
    }
  }
});
window.vm = vm;

console.log(vm.items);
console.log(vm.totalPrice); // 算出プロパティはプロパティとしてアクセス可能

f:id:jotaki:20190124210333p:plain