基礎から学ぶ Vue.js を読んでみました

Vue.js を最近使い始めまして、基礎的なこと理解するために下記の書籍を読みました。重要なことを忘れないようにまとめておきます。











*参考サイト

  • Vue.js 日本公式サイト
  • STUDIO
    ウェブデザインプラットフォームサービスです。デザインを作成するとコードに変換してくれるので、コーディング作業をしなくてもWebサイトを公開することができます。


*Vue.js の基本

  • データ駆動
    構造の本体はDOMでなくJavaScriptのデータであることをデータ駆動といいます。
  • mount
    配置する要素とアプリケーションを紐付けることです。
  • データバインディング
    データと描画を同期させることです。
  • ディレクティブ
    v-から始まる値のことで指定した値はJavaScriptの変数になります。(それ以外は文字列)
  • v-for
    リストの変数の描画に使います。
  • v-on
    クリックしたとき、選択要素が変わったときなどのDOMイベントを受け取るときに使います。
  • v-model
    データフォームの入力項目をバインドするときに使います。.numberオプションを付けることで数値として受け取ることもできます。
  • v-if
    条件分岐に使います。<transition>タグを使うと、テンプレート内の要素にCSSトランジションやアニメーションを入れることができます。
<button v-on:click="show=!show">チェンジ</button>
<transition>
 <p v-if="show">表示された!</p>
</transition>
var app = new Vue({
 el: '#app'
 data: {
  show: true
 }
})
.v-enter-active, .v-leave-active {
 transition: opacity 1s;
}
.v-enter, v-leave-to {
 opacity: 0;
}


*データバインディング

  • dataに定義したプロパティの値を表示するには二重括弧 {{ }}で囲みます。(マスタッシュとよばれる記法)二重括弧に式は入れることができません。
  • 長い式になる場合は算出プロパティに定義します。
  • 文字列や数値の変換にはフィルタを使います。
  • 属性にはマスタッシュ記法は使えません。(例:value="{{ message }}"
  • 属性へのバインディングはv-bindを使います。(v-bind:value="message")省略して :value="message" とも書けます。
  • {{ $data }}を出力することで、data全体の状態をデバッグすることができます。
  • コールバック関数のなかで this.message などプロパティを使うことができません。this がインスタンスでなく window を指してしまうからです。なのであらかじめ別の変数に代入しておくと使うことができます。bind で this を紐づけて使う方法もあります。
  • クラスとスタイル属性へのデータバインディングも可能です。
  • ハイフンを含む場合は ' で囲みます。
  • クラスの条件に3項演算子も使うことができます。(でも算出プロパティでやるほうが良いです)
<p v-bind:class="[isActive ? 'active' : 'normal', otherClass]">三項演算子</p>
  • dataオプションにスタイルのオブジェクトを定義しておくこともできます。
  • v-bindは特定要素を指定したものと併用することができます。
  • SVGを使ってグラフィカルなUIを作成することができます。


*条件分岐

  • v-if条件を満たさなかったらDOMごと削除します。
  • v-show条件を満たさなかったら非表示になるだけです。(切り替えの頻度が高い場合に使います)
  • 複数の要素を条件によって切り替える場合は<template>タグを使ってグループ化します。(v-showは使えません)
  • v-else-ifv-elseも使うことができます。
  • ユニークなkeyを指定することもできます。
<ul>
 <li v-for="item in list" v-bind:key="item.id">
  {{ item.id }} {{ item.name }} {{ item.hp }}
 </li>
</ul>


*リストデータ

  • リストは文字列だけでなく、idも付与したオブジェクトにするのが良いです。(でも表示するだけだったら id は不要です)
  • リストを更新するときはv-bind:keyでキー属性を指定することが推奨されています。(要素の並び替えや削除をするときに必要です)
  • 配列インデックスを受け取ることができます。
<li v-for="(item, index) in list">Hello</li>
  • リストの絞り込みは算出プロパティが適しています。
  • プロパティの更新は可能ですが、配列要素の更新はできません。
  • リストは filter を使ってリストそのものを置き換えることができます。
  • 文字列にv-forをすると1文字ずつ描画します。(テキストアニメーションに活用できます)
  • this.$setを使ってリアクティブデータとして新しく追加することができます。
// activeプロパティを追加
created: function() {
 this.list.forEach(function(item) {
  // item:更新するデータ, 'active':キー, false:新しい値
  this.$set(item, 'active', false)
 }, this)
}


*DOMを参照

  • インスタンスプロパティの$el$refsを使用します。
  • 一時的な変更のため上書きされることもあります。
  • DOMを参照するため、mounted以降でないと使用できません。
  • $el
    ルートやコンポーネントのテンプレートを囲んでいるルート要素を参照することができます。
  • $refs
    ルート以外の要素を参照します。
## 要素にsampleという名前を付与
<p ref="sample">Hello</p>
console.log(this.$ref.sample)


*テンプレート制御

  • v-pre
    HTMLのコンパイルをスキップして静的コンテンツとして扱います。
  • v-once
    1回だけ評価し、以降のリアクティブを解除してDOM更新できないようにします。
  • v-text
    マスタッシュ記法の代わりに表示するときに使います。
  • v-cloak
    インスタンスの準備が終わると自動的に取り除かれます。


*イベントハンドリング

  • v-on
    js のaddEventListenerや jQuery の$(element).onの代わりに使います。省略して@を使うことができます。値に js の式を指定することもできます。
  • $event
    イベントオブジェクトを使用するために使います。
  • スクロールやマウスホイールもハンドルすることができます。
<div v-on:scroll="handler">
<div v-on:mousewheel="handler">
  • v-modelでも同様のことができますが、v-onを使うことで入力内容を確認してからデータに代入することができます。
  • .prevent
    この修飾子を付けることで、リンクの遷移や submit など元の処理をキャンセルすることができます。
  • .self
    要素が自分自身のときだけハンドラが呼び出されます。(モーダルウィンドウ の背景部分をクリックして閉じる場合などに使用します)
  • .enter
    特定のキーコード入力でハンドラが呼び出されます。


*フォーム入力バインディング

  • v-model
    フォームの入力値と選択値を同期させる双方向データバインディングをするために使います。確定するまでデータを更新しません。(確定前に更新したい場合はinputイベントを使います)
  • テキストエリアやチェックボックスにv-modelを使います。
  • ファイルデータにはv-modelを使えないのでv-on:changeを使います。
  • rangecolorなどHTML5の入力タイプも使用できます。
<input type="range" v-model.number="val">
  • .number
    値を数値に変換する修飾子です。
  • .trim
    空白やスペースを削除する修飾子です。


*算出プロパティ

  • computed
    任意のデータを返す関数を定義します。これを算出プロパティといいます。基本的にもとのデータに影響を与えない処理を定義します。
  • プロパティとして定義した関数名を{{ halfWidth }}として使えます。
  • メソッドからだとthis.halfWidthとして使います。
  • 関数の中で別の関数を使うこともできます。
  • ゲッターとセッターを定義できます。
get: function() { return ...},
set: function(val) { this.width = ... }
  • リアクティブなデータでないとキャッシュされません。(ランダム関数などはずっと同じ値になります)キャッシュしたい場合は method に定義します。
  • 複雑な処理に向いています。(リストの絞り込みなど)
  • sortは元のデータを直接操作するため使うべきではありません。(this.list.slice(0).sortや Lodash を使います)


*ウォッチャ

  • 特定のデータを監視して、変化があった場合に処理を自動的に実行します。
  • 新しい値と古い値を引数にして受け取ることもできます。
  • オプションdeepでネスとされたオブジェクトも監視できます。
watch : {
 監視するデータ: function(新しい値, 古い値) {
 },
 deep: true
}
  • オプションで追加する方法でなく、インスタンスメソッドで登録することもできます。
created: function() {
 this.$watch('value', function(newVal, oldVal)) { 
  ...
 }
}
  • unwatch()
    インスタンスメソッドで登録する場合は、解除することもできます。これによって1度しか動かないウォッチャを定義することができます。
var unwatch = this.$watch('value', handler)
unwatch()
  • 非同期通信などコストの高い処理を呼び出すとパフォーマンスが下がるので、setTimeoutやLodashのdebounce()などを使って実行頻度を制御します。(実行から指定ミリ秒が過ぎた場合にコールバックを呼び出すメソッドです)
watch: {
 value: _.debounce(function(newVal) {
  console.log(newVal)
 }),
 500)
}


*フィルタ

  • 文字数を丸めたり数字にカンマを入れるといったテキストの変換処理を行うための機能です。渡された値を別の文字列にして変換します。
  • thisへのアクセスはできません。
  • 引数も持たせることができます。
## message:適用したい文字列 | フィルタ名
{{ message | filter(foo, 100) }}
  • v-bindで使うこともできます。
<div v-bind:id="price" | localNum></div>
  • グローバルへ登録することで全てのコンポーネントから使用することもできます。
Vue.filter('capitalize', function (value) {  
 if (!value) return ''  
 value = value.toString()  
 return value.charAt(0).toUpperCase() + value.slice(1)  
})  
  
new Vue({  
 // ...  
})


*カスタムディレクティブ

  • v-bindのディレクティブを自作するための機能です。
  • 登録したメソッドにv-を付けて使います。
  • directivesオプションに登録することで特定のコンポーネント内でのみ使用できます。
directives: {
 focus: {
  inserted: function(el) {
   el.focus()
  }
 }
}
  • グローバルに登録したい場合はVue.directive()を使います。(全てのコンポーネントから使用できます)
Vue.directive('focus', {
 inserted: function(el) {
  el.focus()
 }
})
  • デフォルトでbindinsertedといった要素の更新や削除によるフックを使うことができます。
  • 各フックはelbindingといった引数を受け取ることができます。
  • el
    ディレクティブが付与されている要素です。
  • binding
    バインドされた値、引数、修飾子のオブジェクトです。
  • vnode
    要素に対するVNodeです。
  • oldVnode
    更新前のVNodeです。(update のみで使用できます)
directives: {
 bind: function(el, binding) {
  ...
 },
 inserted: function(el) {
  ...
 },
 update: function(el) {
  ...
 }
}
前の状態と比較して処理を行うこともできます。
  • arg -->引数
  • modifiers --> 修飾子のオブジェクト
  • value --> 新しい値
  • oldValue --> 古い値(update のみで使用可)
directives: {
 video(el, binding) {
  if (binding.value !== binding.oldValue) {
   binding.value ? el.play() : el.pause()
  }
 }
}


*更新後のDOMにアクセス

  • nextTick
    DOMが更新された後に処理を行います。
    更新後のDOMの高さを取得するときなどに使います。
watch: {
 list: function() {
  ...
  this.$nextTick(function() {
   ...
  })
 }
}


*コンポーネント

  • 親に対して子となるコンポーネントを登録することができます。
Vue.component('my-component', {
 template: '<p>Vue.js</p>'
})
<div id="app">
 ## カスタムタグが置きかわります
 <my-component></my-component>
</div>
  • ローカルで登録する場合は components オプションに定義します。
components: {
 'my-component': Vue.js
}
  • dataはオブジェクトを返す関数にする必要があります。
data: function() {
 return {
  message: 'Hello!'
 }
}

  • props
    子コンポーネントにデータを渡します。(受け取ったデータを更新することはできません)データを更新したい場合は、算出プロパティを使って新しいデータを作成します。
Vue.component('comp-child', {
 template: '<p>{{ val }}</p>',
 props: ['val']
})
  • $emit
    子から親にデータを渡す場合に使います。
## 親側のテンプレートでイベントをハンドル
<comp-child v-on:childs-event="parentsMethod"></comp-child>
## 子がイベントを起こす
template: '<button v-on:click="handleClick">イベント</button>'
methods: {
 handleClick: function() {
  this.$emit('childs-event')
 }
}
## 親側で受け取る (登録しておいたハンドルを呼び出す)
methods: {
 parentsMethod: function() {
  ...
 }
}
  • $refs
    親から子コンポーネントのインスタンスに直接アクセスしたいときに使います。
<comp-child ref="child">
this.$refs.child.$emit('open')
  • 子はインスタンスメソッド$onを使って自分自身のイベントをハンドルすることができます。
created: function() {
 this.$on('open', function() {
  ...
 })
}
  • 子コンポーネントの$emitの引数は、$event変数で親側で使うことができます。


*スロット

  • 親コンポーネント側から子コンポーネントのテンプレートの一部を差し込む機能です。
## 親側のテンプレート
<comp-child>スロット</comp-child>
## 子側のテンプレート
<div class="comp-child">
 <slot></slot>
</div>
  • コンテンツを埋め込んだりテンプレートの一部をカスタマイズすることができます。
  • 親側で定義したコンテンツを子に合体させます。
  • 複数の要素をグループ化したい場合は <template>タグをスロットとして使うことができます。
<comp-child>
 <template slot="text1">テキスト</template>
</comp-child>
  • ディレクティブや属性は使用できません。
  • <slot-scope>で親側で子のデータを受け取ることができます。


*双方向データバインディング

  • v-model
    コンポーネントに対してデータバインディングすることで、input イベントを $emitすることができます。
## 親側のテンプレート
<my-calemdar v-model="date"></my-calemdar>
## 子側のメソッド (dateプロパティに引数を代入)
this.$emit('input', '2018-01-01')
  • デフォルトの設定では value をプロパティ、 input をイベントとして使用しますが、別のイベントを使いたい場合はmodelオプションを書くことで設定をカスタマイズすることができます。
model: {
 props: 'current',
 event: 'change'
},
props: { current: String },
created: function() {
 this.$emit('change', '2018-01-01')
}
  • v-model に.sync修飾子を付けることで複数の属性を同期させることができます。


*規則

  • コンポーネントの名前はハイフンを1つ以上含むケバブケースにする必要があります。
    <my-component></my-component>
  • isの属性
    <table><select>は内部にもてる要素が制限されるため、回避策として isの属性を使って付与された要素をコンポーネントに置き換えます。
<table>
 <tr is="my-row"></tr>
</table>
  • v-if について、カスタムタグだとfalseの場合はコンポーネントは破棄されるが、ルートタグだと破棄されません。<keep-alive>タグを使うことで破棄されないようにできます。


*動的コンポーネント

v-bind:isでコンポーネントを指定することで、コンポーネントの切り替えをすることができる。
## 親コンポーネントでバインド
<div v-bind:is="component"></div>
## 子コンポーネンントで使用
computed: {
 component: function() {
  ...
 }
}


*共通処理

  • Mixin
    コンポーネントのコンスタラクタのオプションを抜き出して共通処理として登録します。
## Mixinを定義
var mixin = {
 created: function() {
  ...
 },
 methods: {
  hello: function() {
   ...
  }
 }
}
## Mixinを登録
Vue.component('my-component-a', {
 mixins: [mixin],
 template: '<p>MyComponentA</p>'
})


*トランジション

  • CSSのトランジションやアニメーションを簡単に実装する機能です。
  • <transition>タグで囲むだけで使えるようになります。
  • name 属性で例としてdemo指定すると、下記のようなCSSクラスを付与することができます。
.demo-enter-active, .demo-leave-active {
 ...
}
.demo-enter, .demo-leave-to {
 ...
}
  • appear属性を付けることで、初期描画時にもトランジションを行うようになります。
<transition appear>
 <div v-if="show">sample</div>
</transition>
  • トランジションクラスは追加される Enter 系と削除される Leave 系が用意されています。
  • position: absolute
    要素を重ねて表示させたい場合にCSSクラスで指定します。
  • in-out, out-in
    mode属性に指定することで、Enter と Leave トランジションのタイミングを変更することができます。
  • 特定のデータの変化をトリガにして発動させたい場合は、<transition>の中でv-bind:keyなどとすると、指定した要素が変更されるたびに切り替えることができます。
<transition>
 <div v-bind:key="count">{{ count }}</div>
</transition>
  • リストトランジションを使うことで、移動のアニメーションを行うことができます。必ずv-bind:keyでキーを設定する必要があります。
<transition-group name="list" tag="ul">
 <li v-for="item in list" v-bind:key="item.id"></li>
</transition-group>
  • .v-move
    リストトランジションで順番が動いたときに.v-moveというトランジションのCSSクラス付与されます。この中でtransition:を定義することで移動アニメーションを行うことができます。
.v-move {
 transition: tramsform 1s;
} 
  • :not
    CSSクラスに指定することで、特定のスタイルを排除することができます。
  • トランジションをイベントフックとして動かすことができます。その場合はv-on:after-enter=とフックを指定します。
  • Enter系のフック、Leave系のフックが用意されています。(before-enter, enter, after-enter など)
  • v-bind:css="false"を指定すると、通常のトランジションクラスの処理を行わないようになります。


*単一ファイルコンポーネント

<style>タグにscopedオプションを付けることで、そのテンプレート内でしかスタイルが適用されないようにすることができます。こうすることでクラス名の衝突を防ぐことができます。


*ES2015モジュール

Vue Routerを使うときに必要になる.js拡張子ファイルの書き方です。


*Vuex

  • データとその状態を一元管理するための拡張ライブラリです。
  • 使用している場所で常に同期されます。
  • srcフォルダにstore.jsを作成して、Vuex.Store コンストラクタでインスタンスを作成します。
  • state で状態の参照、getter でデータを取得、mutations で更新、actions で非同期処理、dispatch で登録されているアクションの呼び出しを行います。
  • 非同期処理はコミット前に行う必要があります。
  • v-modelを使うとエラーになってしまうので、算出プロパティのセッターで$store.dispatch()を使うのが良いです。
  • コンポーネントとストアをバインドするヘルパーが用意されていて、mapStore, mapGettersなどを使うことができます。
  • modules オプションに登録することで、ユーザー情報など関連した情報ごとに分割して登録することができます。分割したモジュールごとにstoreに登録し、アクセスもモジュールごとに行います。
  • update や add などはどの処理でも使われることが多く、名前が衝突しがちです。これを回避するために、namespacedオプションをtrueに設定し、commit(‘ネームスペース/タイプ’)でネームスペースを指定して呼び出すことができます。
const moduleA = {
 namespaced: true,
 mutations: {
  update(state) {...}
 }
}
const moduleB = {
 namespaced: true,
 mutations: {
  update(state) {...}
 }
}
## moduleAを呼び出し
store.commit('moduleA/update')
  • stateを関数にしてモジュールを再利用することもできます。
const moduleA = {
 namespaced: true,
 state() {
  return {
   ...
  }
 }
}
  • state.watch()でストアの状態を監視することができます。
    mutation や actionの呼び出しにフックすることもできます。


*Vue Router

  • SPA(シングルページアプリケーション)を構築するための拡張ライブラリです。
  • コンポーネントとURLを紐づけてコンテンツを生成します。
  • router.jsを作成し、パスをマッピングします。
  • デフォルトではハッシュモードになっていて、自動的にURLに#が付与されてしまうので、mode: 'history'#が付かないヒストリーモードに設定することができます。この場合、同時にサーバー側の設定も必要になります。
  • this.$routeでマッチしたルート情報を参照することができます。


*書籍を読んだ感想

Vue.js の公式ドキュメントを読んだだけでは理解できなかった部分がたくさんあったのですが、この書籍ではコード例と丁寧な言葉で詳しく書かれていたので大変わかりやすかったです。
共通化などについても説明があるので、この書籍の内容をしっかり理解すれば、実際のプロジェクトでも大いに役立つと思いました。何度も読み直して理解を深めたいと思う書籍でした。

Previous
Next Post »

人気の投稿