まとめ
・算出プロパティは結果をキャッシュしてくれる
・データの状態に対して処理をしたいならウォッチャ
・カスタムディレクティブは監視をしながらDOMの操作ができる
・更新後のDOMにアクセスしたいならnextTick
1. 算出プロパティで処理を含むデータを作成
算出プロパティ(Computed 処理を含むデータ)
<p>{{ width }} の半分は {{ halfWidth }}</p>
new Vue({
el: '#app',
data: {
width: 800
},
computed: {
//算出プロパティhalfWidthを定義
halfWidth: function() {
return this.width / 2
}
}
})
算出プロパティの組み合わせ
<p>X: {{ halfPoint.x }}</p>
<p>Y: {{ halfPoint.y }}</p>
new Vue({
el: '#app',
data: {
width: 800,
height: 600
},
computed: {
halfWidth: function() {
return this.width / 2
},
halfHeight: function() {
return this.height / 2
},
// 「width × height」の中心座標をオブジェクトで返す
halfPoint: function() {
return {
x: this.halfWidth,
y: this.halfHeight
}
}
}
})
ゲッターとセッター
<input v-model.number="width"> {{ width }}
<input v-model.number="halfWidth"> {{ halfWidth }}
new Vue({
el: '#app',
data: {
width: 800
},
computed: {
halfWidth: {
get: function() {
return this.width / 2
},
//halfWidth の2倍の数値を width に代入する
set: function(val) {
this.width = val * 2
}
}
}
})
算出プロパティのキャッシュ機能
算出プロパティ…リアクティブな依存データに基づき、結果をキャッシュする。キャッシュを再構築するトリガになるのはリアクティブなデータのみ
メソッド…キャッシュされない
<!-- 算出プロパティ:キャッシュされるため、何度使用しても同じ結果 -->
<ol>
<li>{{ computedData }}</li><!-- 0.8205563271901375 -->
<li>{{ computedData }}</li><!-- 0.8205563271901375 -->
</ol>
<!-- メソッド:キャッシュされない。呼び出すたび変わる -->
<ol>
<li>{{ methodsData() }}</li>0.13761479619266637
<li>{{ methodsData() }}</li>0.45429414765683895
</ol>
new Vue({
el: '#app',
computed: {
computedData: function() { return Math.random() }
},
methods: {
methodsData: function() { return Math.random() }
}
})
サンプルコード:リストの絞り込み・ソート機能
<div id="app">
<input v-model.number="budget"> 円以下に絞り込む
<input v-model.number="limit"> 件を表示
<button v-on:click="order=!order">切り替え</button>
<p>{{ matched.length }} 件中 {{ limited.length }} 件を表示中</p>
<ul>
<!-- v-forでは最終結果、算出プロパティのlimitedを使用する -->
<li v-for="item in limited" v-bind:key="item.id">
{{ item.name }} {{ item.price }}円
</li>
</ul>
</div>
new Vue({
el: '#app',
data: {
//フォームの入力と紐付けるデータ
budget: 300,
//表示件数
limit: 2,
//もとになるリスト
list: [
{ id: 1, name: 'りんご', price: 100 },
{ id: 2, name: 'ばなな', price: 200 },
{ id: 3, name: 'いちご', price: 400 },
{ id: 4, name: 'おれんじ', price: 300 },
{ id: 5, name: 'めろん', price: 500 }
]
},
computed: {
//budget以下のリストを返す算出プロパティ
matched: function () {
return this.list.filter(function (el) {
return el.price <= this.budget
}, this)
},
//matchedで返ったデータをlimit件返す算出プロパティ
limited: function () {
return this.matched.slice(0, this.limit)
}
}
})
##2. ウォッチャでデータを監視して処理を自動化する
ウォッチャ
new Vue({
...
watch: {
監視するデータ: function (新しい値, 古い値) {
valueが変化したときに行いたい処理
},
'item.value': function (newVal, oldVal) {
//オブジェクトのプロパティも監視できる
}
}
})
ウォッチャ(オプションあり)
new Vue({
...
watch: {
list: {
handler: function (newVal, oldVal) {
//listが変化したときに行いたい処理
},
deep: true,//ネストされたオブジェクトも監視するか
immediate: true //初期読み込み時にも呼び出すか
}
}
})
インスタンスメソッドでの登録(オプションなし)
this.$watch(監視するデータ, ハンドラ, オプション(任意))
this.$watch('value', function(newVal, oldVal) {
//...
})
インスタンスメソッドでの登録(オプションあり)
this.$watch('value', function (newVal, oldVal) {
...
}, {
immediate: true,
deep: true
})
一度だけ動作するウォッチャ(unwatchで解除する)
new Vue({
el: '#app',
data: {
edited: false,
list: [
{ id: 1, name: 'りんご', price: 100 },
{ id: 2, name: 'ばなな', price: 200 },
]
},
created: function() {
var unwatch = this.$watch('list', function () {
//listが編集されたことを記録する
this.edited = true
//監視を解除
unwatch()
}, {
deep: true
})
}
})
実行頻度の制御p130
フォームの入力などによって監視する値が高頻度で変化する→ウオッチャの実行頻度を制御する
<!-- ※lodash.min.jsを読み込む -->
<input type="text" v-model="value">
new Vue({
el: '#app',
data: {
value: '編集してみてね'
},
watch: {
//実行から指定ミリ秒がすぎた場合にコールバックを呼び出す
value: _.debounce(function (newVal) {
//ここへコストの高い処理を書く
},
//valueの変化が終わるのを待つ時間をミリ秒で指定
500)
}
})
複数の値を監視する
//インスタンスメソッドを使い監視対象を関数にして登録
this.$watch( function() {
return [this.width, this.height]
}, function() {
//widthまたはheightが変化した時の処理
})
//オプションに登録する場合は、算出プロパティを監視する
cpmputed: {
watchTarget: function() {
return [this.width, this.height]
}
},
watch: {
watchTarget: function() { ... }
}
オブジェクト型の古い値との比較方法 p132
フォームを監視してAPIからデータを取得(検索フォームに応用できる)
<!-- ※axios.min.jsを読み込む -->
<div id="app">
<select v-model="current">
<option v-for="topic in topics" v-bind:value="topic.value">
{{ topic.name }}
</option>
</select>
<div v-for="item in list">{{ item.full_name }}</div>
</div>
new Vue({
el: '#app',
data: {
list: [],
current: '',
topics: [
{ value: 'vue', name: 'Vue.js' },
{ value: 'jQuery', name: 'jQuery' }
]
},
watch: {
current: function (val) {
//GitHubのAPIからトピックのリポジトリを検索
axios.get('https:api.github.com/search/repositories', {
params: {
q: 'topic:' + val
}
}).then(function (response) {
this.list = response.data.items
}.bind(this))
}
},
})
3. フィルタの使い方
<!-- Mustacheで使用する場合 -->
<div>{{ 対象のデータ | フィルタの名前 }}</div>
<!-- v-bindで使用する場合 -->
<div v-bind:id=" 対象のデータ | フィルタの名前 "></div>
ローカルでフィルタを使う
<p>{{ price | localNum }}円</p>
new Vue({
el: '#app',
data: {
price: 19800
},
filters: {
localeNum: function (val) {
return val.toLocaleString()
}
}
})
//グローバルでフィルタを使う(全てのコンポーネントから使用できる)
Vue.filter('localNum', function(val) {
return val.toLocalString()
})
フィルタに引数を持たせる
<p>{{ message | filter(foo, 100) }}</p>
new Vue({
filters: function(message, foo, num) {
console.log(message, foo, num)
}
})
複数のフィルタを繋げて使用
<p>180 度は {{ 180 | radian | round }} ラジアンだよ</p>
new Vue({
el: '#app',
filters: {
//小数点以下を第2位に丸めるフィルタ
round: function (val) {
return Math.round(val * 100) / 100
},
//度からラジアンに変換するフィルタ
radian: function (val) {
return val * Math.PI / 180
}
}
})
4. カスタムディレクティブ
コンポーネントのdirectivesオプションに登録することで、特定のコンポーネント内で使用できる
<input type="text" v-focus>
new Vue({
el: '#app',
directives: {
focus: {
//紐付いている要素がDOMに挿入されるとき
inserted: function (el) {
el.focus() //要素にフォーカスを当てる
}
}
}
})
// グローバルへの登録
Vue.directive('focus', {
inserted: function(el){
el.focus()
}
})
使用可能なフック
Vue.directive('example', {
//ディレクティブが初めて要素と紐づいた時
bind: function (el, binding) {
console.log('v-example bind')
},
//紐づいた要素が親Nodeに挿入された時
inserted: function (el, binding) {
console.log('v-example inserted')
},
//紐付いた要素を包含しているコンポーネントのVNodeが更新された時
update: function (el, binding) {
console.log('v-example update')
},
//包含しているコンポーネントと子コンポーネントのVNodeが更新された時
componentUpdated: function (el, binding) {
console.log('v-example componentUpdated')
},
//紐付いていた要素からディレクティブから削除される時
unbind: function (el, binding) {
console.log('v-example unbind')
}
})
//updateおよびcomponentUpdate:
//コンポーネントの仮想DOMが更新された時に呼び出される
フックの引数
引数 | 作用 |
---|---|
el | ディレクティブが付与されている要素 |
binding | バインドされた値、引数、修飾子のオブジェクト |
vnode | 要素に対応するVNode |
oldVnode | 更新前のVNode(updateおよびcomponentUpdatedのみ使用可) |
フックの関数による省略記法
第2引数として関数を渡す:bindとupdateにフックされる
Vue.directive('example', function(el, binding, vnode, oldVnode) {
//bindとupdateで呼び出される処理
})
例)動画の再生を操作する
<div id="app">
<button v-on:click="video1=true">再生</button>
<button v-on:click="video1=false">再生</button>
<video src="movie1.mp4 v-video="video1">再生</video>
<button v-on:click="video2=true">再生</button>
<button v-on:click="video2=false">再生</button>
<video src="movie1.mp4 v-video="video2">再生</video>
</div>
new Vue({
el: '#app',
data: {
video1: false,
video2: false
},
directives: {
video(el, binding) {
if (binding.value !== binding.oldValue) {前の処理と比較して処理を行う
binding.value ? el.play() : el:pause()
}
}
}
})
第2引数bindingは次のプロパティを含むオブジェクトになる
1 | 2 |
---|---|
arg | 引数 |
modifiers | 修飾子のオブジェクト |
value | 新しい値 |
oldValue | 古い値(updateおよびcomponentUpdatedのみ使用可) |
5. nextTickで更新後のDOMにアクセスする
<button v-on:click="list.push(list.length+1)">追加</button>
<ul ref="list">
<li v-for="item in list">{{ item }}</li>
</ul>
new Vue({
el: '#app',
data: {
list: []
},
watch: {
list: function () {
//更新後のul要素の高さを取得できない…
console.log('通常:', this.$refs.list.offsetHeight)
//nextTickを使えばできる!
this.$nextTick(function () {
console.log('nextTick:', this.$refs.list.offsetHeight)
})
}
}
})