Aikの技術日記

技術的な進捗とか成果とかを細々と投稿するブログです。時々雑記も。

Nuxt.js使ってみた Part5~小ネタ:propsとdataの違い編~

はじめに

お久しぶり?です、筆者です。

前回は、Part2で実施したチュートリアル実践中に気になり深掘りしたこと3つ:

  • Vuexとは?
  • computedとmethodsの違い
  • propsとdataの違い

…のうち2番目の「computedとmethodsの違い」についてまとめてみました。
今回では、最後となる「propsとdataの違い」についてを見ていこうかなと。

今回もこちらの公式記事の内容そのまんまですが…。
なぜか公式記事って頭に入りにくいんですよね。
個人的にはこうして、一度ブログとしてまとめた方が頭に入りやすく…。

まぁ何が言いたいかというと、今回の記事の内容はほぼほぼ公式記事の内容となっているということです。
それでは行きましょう。

propsとdataの違い

チュートリアル実施時点では、propsとdataの違いについて筆者は…
「他コンポーネントからv-bindで値をバインドできるかできないか」くらいにしか理解してなかったので…。

ここいらでしっかりと理解しておいた方が後々苦労しないかなと思い、見てみることにしました。

まず、props(プロパティ)はコンポーネントから受け取るデータを定義するものです。
データは必ず親から子供に歯科渡すことができず、子供から親に渡すことはできません。(単方向データフロー)
次に、dataはVueインスタンスで利用するデータオブジェクトを定義するものです。

…うん、公式記事なり色々読んでもこれくらいしかわからず…。
「props data vue 違い」で調べても、上記くらいの内容で言い表せられる内容しかなかったのです。

不思議に思ってさらに調査していくと…どうも「プロパティとdataが示す前提となる範囲が異なるっぽい」という結論にいたり。

プロパティはあくまで「親子コンポーネント間の中でのお話」で、dataは「Vueインスタンスの中でのお話」で出てくるものでありそうです。

Vueインスタンスを定義した時に、各インスタンス内に存在するものがdata…。
Vueコンポーネント間でデータを受渡しする時に話題となるのがプロパティ…。
といった形でしょうか。

個人的には、異なるコンポーネント間でデータをやり取りする様なコードを書くことが多く感じるため…。
dataについてはあんまし意識することはないのかなと思いつつ。

いずれ意識することになるかもしれませんが…多分Vue.jsをある程度習熟しないと意識しないかもですね。

また、ここまで調査するのに面白い小ネタがいっぱい見つかりましたので…。
次から余談として、プロパティとdataに関する小ネタを記していこうかと。

余談: プロパティには型を定義しよう

プロパティには下記の様に、型を定義した方がいいとのこと。

props: {
  title: String,
  likes: Number,
  isPublished: Boolean,
  commentIds: Array,
  author: Object,
  callback: Function,
  contactsPromise: Promise // or any other constructor
}

こうすることで、プロパティの各値の説明ができる他、型を間違えていたら警告もしてくれるとのこと。
プロパティはHTMLに直接記載して渡す形になるので、型の違い等になかなか気付きにくいなぁと思う次第ですが…。
警告を出してくれるなら、その心配もなくなりそうですね。

余談: プロパティを渡す時になぜ小文字で定義しないといけないのか

Vue.jsを書いている時にずっと思っていたこと…「プロパティを渡す時に、なぜ値をキャメルケースではなくケバブケースで書かないといけないのか」について。
例えば下記の様な感じで…:

<!-- HTML 内ではケバブケース -->
<blog-post post-title="hello!"></blog-post>
Vue.component('blog-post', {
  // JavaScript 内ではキャメルケース
  props: ['postTitle'],
  template: '<h3>{{ postTitle }}</h3>'
})

これについて、公式記事に言及があったので備忘録がわりにメモを。

HTMLの属性名は大文字小文字を区別せず、ブラウザは全ての大文字を小文字として解釈します。
つまりは、DOM(HTML)のテンプレート内においては、キャメルケースのプロパティはケバブケース(ハイフンで区切ったもの)を使用する必要があります。

参考: プロパティ — Vue.js

余談:v-bindを付けてプロパティを渡したときの挙動の差

プロパティは静的なものであればv-bindをつける必要はないですが、動的な値を渡す場合はv-bindを付与する必要があります。

<!-- 静的な値渡し -->
<blog-post title="My journey with Vue"></blog-post>
<!-- 動的な値渡し -->
<blog-post v-bind:likes="post.likes"></blog-post>

また、「文字列ではなくJavaScript式として値を渡したい!」なんて時も、v-bindを付与する必要があります。

<blog-post v-bind:is-published="false"></blog-post>

なお、何も値を指定しない場合は、trueを渡すのと等価となります。

<!-- trueが渡される -->
<blog-post is-published></blog-post>

余談: プロパティにオブジェクト丸ごと値渡し

プロパティには「そのプロパティに内包されるデータを丸ごと渡す」という機能があるそうで。
例えば下記の様な型を持つデータがあるとして:

post: {
  id: 1,
  title: 'My Journey with Vue'
}

上記のデータを子コンポーネントに渡す際は、下記の様に記述すると思いますが…。

<blog-post
  v-bind:id="post.id"
  v-bind:title="post.title"
></blog-post>

下記の様に記載しても、同じ様に動いてくれるそうです。ベンリ!!

<blog-post v-bind="post"></blog-post>

dataを定義するときの注意点

dataは下記の様に直接オブジェクトを内包するのではなく…:

data: {
  count: 0
}

下記の様に、関数にしないといけないそうです。

data: function () {
  return {
    count: 0
  }
}

こうしないと、dataの中身が同一コンポーネント間で共通のものになってしまいます
この辺りは公式記事をみるととても分かりやすいです: コンポーネントの基本 — Vue.js

おわりに

今回はチュートリアルで作成したコードのカスタマイズの中で気になった項目3つ:

  • Vuexとは?
  • computedとmethodsの違い
  • propsとdataの違い

…の内、最後となる「propsとdataの違い」について見ていきました。
ひとまず気になることはまとめ終わっちゃったので、今シリーズはいったん終わりになるかと。

もしまた気になることができたら、再度再開するかもしれません。
((まーその頃にはこのシリーズの存在を忘れてそうなので、別記事としてまとめちゃいそうですが

次はClean Architecture系の記事を書いていくかも…。
それでは|д゚*)

Nuxt.js使ってみた Part4~小ネタ:computedとmethodsの違い編~

はじめに

お久しぶり?です、筆者です。

前回は、Part2で実施したチュートリアル実践中に気になり深掘りしたこと3つ:

  • Vuexとは?
  • computedとmethodsの違い
  • propsとdataの違い

…のうち1番目の「Vuexとは?」についてまとめてみました。

今回では、2番目となる「computedとmethodsの違い」についてを見ていこうかなと。
※結構ボリューム多くまとめちゃったので、ご了承をば…。

それでは行きましょう。

computedとmethodsの違い

公式記事によると、computedは正式には「算出メソッド」、methodsはそのまんま「メソッド」という意だそうで。
jp.vuejs.org

computedとmethodsの違いは、「リアクティブな依存関係にもとづきキャッシュしてくれる」こと。
※リアクティブという単語の意味については、こちらの記事が分かりやすかったです:
リアクティブプログラミングへの理解がイマイチだったのでまとめてみた - UUUM攻殻機動隊(エンジニアブログ)

公式記事の例をまんま持ってきますが…例えば下記の様なコードがあった際:

<p>Reversed message: "{{ reverseMessage() }}"</p>
// コンポーネント内
computed: {
  reverseMessage: function () {
    return this.message.split('').reverse().join('')
  }
}

上記の様にcomputedでreverseMessage()を定義すると、this.messageの値が変化した時にしかreverseMessage()メソッドは再実行されません
つまり、this.messageの値が変化しない限り「再度メソッド実行することなく以前計算された結果を即時に返してくれる」のです。

ただし、下記の様にmethodsで定義してしまうと:

methods: {
  reverseMessage: function () {
    return this.message.split('').reverse().join('')
  }
}

this.messageの値が変化しようがしまいが、reverseMessage()レンダリング時に常に再実行されます。

じゃあmethodsってどういう時に使うのかというと、下記の様な「算出結果をキャッシュして欲しくない処理」に使用するとよいようです。
例えば、下記の様なDate.now()を使用する処理を算出プロパティで書いてしまうと…

computed: {
  now: function () {
    return Date.now()
  }
}

Date.now()の変更はVue.jsでは検知できないため、このメソッドは「実行されてから永遠に返却値が変わらないメソッド」となってしまいます。コマッタ!
こういう関数を使いたいときは、methodsを使えということでしょうね。

補足: watchについて

「値の変更を検知し」「検知されたら実行する」処理をしたい時に…。
Vue.jsでは「監視プロパティ(watch)」というものが提供されています。

ただ、公式Wikiには「大概の場合はcomputedを使う方がいい」とありました。
その理由は下記のコードを見比べればわかるかと:

<div id="demo">{{ fullName }}</div>
// watch使用Ver
var vm = new Vue({
  el: '#demo',
  data: {
    firstName: 'Foo',
    lastName: 'Bar',
    fullName: 'Foo Bar'
  },
  watch: {
    firstName: function (val) {
      this.fullName = val + ' ' + this.lastName
    },
    lastName: function (val) {
      this.fullName = this.firstName + ' ' + val
    }
  }
})
computed使用Ver
var vm = new Vue({
  el: '#demo',
  data: {
    firstName: 'Foo',
    lastName: 'Bar'
  },
  computed: {
    fullName: function () {
      return this.firstName + ' ' + this.lastName
    }
  }
})

上記の例だとfullNamefirstNamelastNameに依存するため、computedでひとまとめに処理した方が良かったですが…。
1つの値によって、アプリケーションの状態が繊細に変化する様な状況ではwatchの方が良さそうですね。

補足: computedを深掘りしてみる

公式記事を元に、computedが具体的にどんな振る舞いをするのかもみてみました。
jp.vuejs.org

というか公式記事、チュートリアルだけでなく内容の深いところまでしっかり書いてくれているからすごく参考になりますね…。
若干説明が分かりにくいので、こうして別途記事にまとめた方が理解しやすいですが。

閑話休題
例えばこんな感じのコードがあるとします:

<div id="example">
  <p>元々のメッセージ: "{{ message }}"</p>
  <p>反転したメッセージ: "{{ reversedMessage }}"</p>
</div>
var vm = new Vue({
  el: '#example',
  data: {
    message: 'Hello'
  },
  computed: {
    // 算出 getter 関数
    reversedMessage: function () {
      // `this` は vm インスタンスを指します
      return this.message.split('').reverse().join('')
    }
  }
})

こういう風に書くことで、reversedMessageは「プロパティvm.reversedMessageに対するgetter関数」として宣言されます。
そしてさらに、この値はvm.messageに常に依存するようになっており。

これにより、Vue.js側がmessagereversedMessageの依存関係を知ることができるため…。
messageが変更されたらmessageに依存している他の処理(ここでいうとreversedMessageですね)も再実行される」様にできるのです!

computedはこの様にして、依存している値と連動できてるんですなぁ。

おわりに

今回はチュートリアルで作成したコードのカスタマイズの中で気になった項目3つ:

  • Vuexとは?
  • computedとmethodsの違い
  • propsとdataの違い

…の内、「computedとmethodsの違い」について見ていきました。
computedとmethodsの他にも、watchについてもまとめちゃうことになりましたね…。

これら3項目について、本記事では「各項目が何を示すのか」に着目してまとめていきましたが…。
純粋に各項目の比較をしたいなら、こちらの記事が分かりやすくておすすめです。
qiita.com

次回記事では、最後となる「propsとdataの違い」について見て行ければと。

それでは|д゚*)

Nuxt.js使ってみた Part3 ~小ネタ:Vuexとは?編~

はじめに

お久しぶり?です、筆者です。

前回はNuxt.jsのチュートリアルを実施し、さらにそのチュートリアルで作成したコードのカスタマイズを行いました。
そして…カスタマイズを行っていく中で、下記のことが気になり深掘りしたので…:

  • Vuexとは?
  • computedとmethodsの違い
  • propsとdataの違い

今回では、上記の内「Vuexとは?」についてを見ていこうかなと。

それでは行きましょう。

Vuexとは

Vuex は Vue.js アプリケーションのための 状態管理パターン + ライブラリです。
これは予測可能な方法によってのみ状態の変異を行うというルールを保証し、アプリケーション内の全てのコンポーネントのための集中型のストアとして機能します。
また Vue 公式の開発ツール拡張と連携し、設定なしでタイムトラベルデバッグやステートのスナップショットのエクスポートやインポートのような高度な機能を提供します。

参考: Vuex とは何か? | Vuex

Vuexとは平たく言うと「アプリケーションの状態を管理するライブラリ」…と言った形です。

使い方としては…。
ログイン機構があるアプリケーションにて「現在ログイン中かどうか」をVuexを使って判断する、と言った形でしょうか。

こちらの記事も読むとより理解が深まるかも…。
qiita.com

最大のメリットは、VuexはVue.jsのコンポーネントシステムから切り離されていることです。
このため、どのコンポーネントから参照しても、常に同じ状態を取得可能です

例えば、今回のチュートリアルコードのように「単一のコンポーネントで全てのデータ管理を行う」様なアプリケーションであればあまり恩恵はうけとれませんが…。

今回のアレンジ後のアプリケーション…具体的には、こういう構造の物を:

<!-- Index.vueコンポーネント -->
<template>
 ... 省略
  <tr v-for="(item, index) in todos" :key="index">
    <td>{{ item.content }}</td>
    <td>{{ item.created }}</td>
    <td><button class="button">{{ item.state }}</button></td>
    <td><button class="button button--delete">削除</button></td>
  </tr>
 ... 省略
</template>

…こういう感じにするように:

<!-- Index.vueコンポーネント -->
<template>
 ... 省略
  <section v-for="(item, index) in display_todos" :key="index">
    <Task
      :task="item"></Task>
  </section>
 ... 省略
</template>

Index.vueコンポーネントが参照するdisplay_todosと同じデータを「Taskコンポーネントからも参照しなければならない」場合の様な…。
「親コンポーネントと子コンポーネントが同一のデータを参照して振る舞いを変える」システムだと、Vuexでデータを構成するだけでとてもいい感じに動きます。

もしVuexを使用しない状態だと…。
例えばTaskコンポーネント内部でデータが変更されたらIndex.vueコンポーネントへその情報を伝播させ、データを更新しなければなりませんからね。

Vuexだと、下記の様にthis.$store.commitを使う事でデータの変更を反映できるので便利です。

  methods: {
    /**
     * 要素を削除する。
     */
    remove: function() {
      this.$store.commit('remove', this.task)
    },
    /**
     * 要素のステータスを変更する。
     */
    changeState: function(){
      this.$store.commit('changeState', this.task)
    },
  },

システム開発をしていると、画面単位では違うデータを見にいく設計(ユーザー一覧画面=ユーザーデータ、タスク一覧画面=タスクデータの様に)にはなりやすいですが…。
コンポーネント単位で違うデータを見にいく設計にはなりずらいでしょうからね。

特にVue.jsだと、「複数のコンポーネントが組み合わさって1つの画面を構成する」様な設計にすることも多いでしょうし。
そういう時に、Vuexは大活躍しそうです。

おわりに

今回はチュートリアルで作成したコードのカスタマイズの中で気になった項目3つ:

  • Vuexとは?
  • computedとmethodsの違い
  • propsとdataの違い

…の内、「Vuexとは?」について見ていきました。
次回記事では、2つめの「computedとmethodsの違い」について見て行ければと。

それでは|д゚*)