Vue.jsで使える便利機能の一つに「watch」オブジェクトがあります。
watchは指定したプロパティの値を監視し、変更があると、指定した処理を自動で実行してくれるすごいヤツです。(watchとは監視の意味です)
watchにはhandlerやdeep, immediateといった記述があるときとない時があります。
handlerって何?、deep, immediateって何?これがあるとwatchの処理の何が変わるの?という疑問を持たれた方も少なくないと思います。
ここでは、watchのhandlerやdeep、immediateの処理について、実例を踏まえて解説しています。
handlerやdeep, immediateとは何か?
いきなりですが、handlerやdeep, immediateとは何か?という本題から解説します。
handlerを理解する前に、まずはdeepやimmediateとは何かについて理解する必要があります。
deepやimmediateとは何か?
deep(ディープ)とimmediate(イミーディエート)はwatchオブジェクトのオプションです。このオプションを使うことで、watchの処理に次のような、処理が加わります。
オプション | 処理内容 |
---|---|
deep | 指定したプロパティの中にある、プロパティの値も監視する(入れ子・ネストしたプロパティ) |
immediate | watchの処理をページ読み込み時にも実行する。 |
なお、オプションは複数同時に設定することもできます。
deep(ディープ)
watchはデフォルトの状態では、指定したプロパティの値しか監視しません。
deepを付けることで、指定したプロパティの中にある、プロパティの値、いわゆる入れ子やネストしたプロパティの値も監視します。
deepは英語で深いという意味です。
immediate(イミーディエート)
watchはデフォルトの状態ではページを読み込んだ時点では処理を実行しません。何かの変化があったときに初めて処理が発動します。
immediateオプションをつけると、初めてページを読み込んだ瞬間に指定したwatchの処理を実行することができます。
immediateとは英語で「即座に」という意味です。
どちらも詳細については実例を踏まえて後述しています。時と場合によって必要になるとても便利なオプションです。
handlerとは何か?
handler(ハンドラー)はdeepやimmediateなどのオプションを使用するときに必要になるオブジェクトです。
handlerとは、処理がどこであるかを示す指定のメソッド名です。詳しくはオプションありのwatchとオプションなしのwatchを見比べるとわかりやすいです。
オプションのないwatch
通常のオプションのないwatchの記述は以下のようになります。ここにhandlerは出てきません。
watch:{
監視するプロパティ名( 新しい値を入れる変数, 古い値を入れる変数 ){
処理
}
}
オプションのあるwatch
watchにオプションを付けると以下のようになります。
watch:{
監視するプロパティ名: {
handler( 新しい値を入れる変数, 古い値を入れる変数 ){
処理
},
オプション名: 真偽値
}
}
オプションは、watchで実行する処理内容と並列に記述する必要があります。
オプションのないwatchの記述の中にオプションを記述しようとすると、記述する場所が存在しません(処理の中にオプションを設定すると、処理の一部とみなされてしまう。)
このため、どれが処理かを示すメソッド名としてhandlerを設置します。
こうすることで処理内容であるhandlerとオプションが上記のように並列に記述できるようになります。
functionを省略した記述
補足ですが、Vue.jsのmethodsやwatchなどのオブジェクトの記述にはfunctionをつける記述と省略する記述の2つがあります。
どちらも処理内容は同じですが、記述が異なるので、初見だと「何が違うの?」と混乱するかもしれません。
オプションなしのwatchのfunctionの有無
オプションなしのwatchでfunctionを使う場合と、使わない場合は以下のようになります。
functionあり
watch:{
監視するプロパティ名: function( 新しい値を入れる変数, 古い値を入れる変数 ){
処理
}
}
functionなし
watch:{
監視するプロパティ名( 新しい値を入れる変数, 古い値を入れる変数 ){
処理
}
}
: function
を省略することができます「:」も省略することに注意してください。
オプションありのwatchのfunctionの有無
オプションありのwatchでfunctionを使う場合と、使わない場合は以下のようになります。
functionあり
watch:{
監視するプロパティ名: {
handler: function( 新しい値を入れる変数, 古い値を入れる変数 ){
処理
},
オプション名: 真偽値
}
}
handlerの後ろに: function
がつきます。
オプション無しのときと違い、監視するプロパティ名の後ろではなく、handlerの後ろにfunctionが付いていることに注意してください。
functionなし
watch:{
監視するプロパティ名: {
handler( 新しい値を入れる変数, 古い値を入れる変数 ){
処理
},
オプション名: 真偽値
}
}
handlerの後ろの: function
を省略することができます「:」も省略することに注意してください。
引数の省略
watchオプションでは処理に対して以下のように引数を2つ指定しています。
監視するプロパティ名( 新しい値を入れる変数, 古い値を入れる変数 )
handler( 新しい値を入れる変数, 古い値を入れる変数 )
第1引数の「新しい値を入れる変数」には変更後の値が入り、第2引数の「古い値を入れる変数」には変更前の元々の値が入ります。
「新しい値」も「古い値」必要ない場合
この2つの引数は必須ではありません。処理の中で「新しい値」も「古い値」の両方ともを使用しない場合は、引数を記述する必要はありません。
watch:{
監視するプロパティ名(){
処理
}
}
なお、第1引数と、第2引数の両方とも(あるいは第1引数のみ)を記述したとしても、処理の中で使わなくても問題ありません。
「新しい値」のみ必要な場合
「新しい値」のみ使用したい場合は、第1引数のみ記述することもできます。
watch:{
監視するプロパティ名: function( 新しい値を入れる変数 ){
処理
}
}
「古い値」のみ必要な場合
「古い値」のみ使用したい場合は、第1引数と、第2引数の両方を記述する必要があります。処理の中で、第1引数を使わなければ問題ありません。
入れ子(ネスト)とは何か?通常のプロパティとの違い
deepオプションで監視するのは、入れ子(ネスト)したプロパティです。
入れ子(ネスト)したプロパティと通常のプロパティの違いは以下のようになっています。
通常のプロパティ(ネストしていない)
通常のプロパティとは、プロパティ名に対して値が1つついているものです。
name: 'Steve'
入れ子の(ネストした)プロパティ
入れ子の(ネストした)プロパティとは、プロパティの値に、プロパティが入っているものです。
user: {
name: 'Steve',
age: '28',
homeTown: '東京'
}
なお、配列の中に入っているプロパティも入れ子(ネスト)とみなされます。
colors:[
{ red: 'あか' },
{ yellow: 'きいろ' },
{ green: 'みどり' }
],
入れ子になっていない階層のwatch
書き方
通常の入れ子になっていないプロパティのwatchは以下のようになります。
watch:{
監視するプロパティ名( 新しい値を入れる変数, 古い値を入れる変数 ){
処理
}
}
実例
dataオブジェクトの中にnameというプロパティをセットして、v-modelを使ったimputで双方向バインディングします。
nameの値が変更されたときに、コンソールに変更内容と元の内容を表示するプログラムは以下のようになります。
var app = new Vue({
el:"#app",
data:{
name: 'Jonson',
watch:{
name( newVal, oldVal ){
console.log( 'newVal:' + newVal, 'oldVal:' + oldVal )
}
}
})
nameプロパティの値を監視して、変更があったら、コンソールに新しい値を「newVal」の横に表示し、元々の古い値を「oldVal」の横に表示するプログラムです。
<body>
<div id="app">
<p><input type="text" v-model.lazy="name" ></p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
</body>
v-modelにlazy修飾子をつけて、一文字づつの変更ではなく、入力確定のみを検出できるようにしています。
ブラウザとコンソールの表示
初期値はv-modelで指定した値が表示されます。
コンソールには何も表示されていません。
↓ 入力値を「田中さん」にします。
コンソールに新しい値が「newVal」の横に表示され、元々の古い値が「oldVal」の横に表示されます。
watchが正常に機能していることがわかります。
deepの使い方実例|入れ子になっている階層のwatch
書き方
値が入れ子になっているプロパティの変更を監視する記述は以下のようになります。
watch:{
監視するプロパティ名: {
handler( 新しい値を入れる変数, 古い値を入れる変数 ){
処理
},
deep: true
}
}
実例
dataオブジェクトの中に入れ子になった値を持つuserというプロパティをセットして、v-modelを使ったimputで双方向バインディングします。
userプロパティの中のプロパティの値のどれかに変更があった場合に、その変更を検知するには、deepオプションを使う必要があります。(deepがないとwatchが働きません)
var app = new Vue({
el:"#app",
data:{
user: {
name: 'Steve',
age: '28',
homeTown: '東京'
}
},
watch:{
user: {
handler( newVal, oldVal ){
console.log( 'newVal:', newVal, 'oldVal:', oldVal )
},
deep: true
}
}
})
userプロパティの値を監視して、変更があったら、コンソールに新しい値を「newVal」の横に表示し、元々の古い値を「oldVal」の横に表示するプログラムです。
<body>
<div id="app">
<p><input type="text" v-model.lazy="user.name" ></p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
</body>
v-modelにlazy修飾子をつけて、一文字づつの変更ではなく、入力確定のみを検出できるようにしています。
ブラウザとコンソールの表示
初期値はv-modelで指定した値が表示されます。
コンソールには何も表示されていません。
↓ 入力値を「田中さん」にします。
コンソールに新しい値が「newVal」の下に表示され、元々の古い値が「oldVal」の下に表示されます。
watchが正常に機能していることがわかります。
deepの実例2|配列の中のオブジェクトの値を監視する
プロパティの値が配列で、その中にプロパティが入っている場合もdeepオプションを使う必要があります。
実例
dataオブジェクトの中に値が配列で、その中にプロパティを持つcolorsというプロパティをセットして、v-modelを使って双方向バインディングします。
colorsプロパティの中のプロパティの値のどれかに変更があった場合に、その変更を検知するには、deepオプションを使う必要があります。(deepがないとwatchが働きません)
var app = new Vue({
el:"#app",
data:{
colors:[
{ red: 'あか' },
{ yellow: 'きいろ' },
{ green: 'みどり' }
],
},
watch:{
colors: {
handler( newVal, oldVal ){
console.log( 'newVal:', newVal, 'oldVal:', oldVal )
},
deep: true
},
}
})
colorsプロパティの値を監視して、変更があったら、コンソールに新しい値を「newVal」の横に表示し、元々の古い値を「oldVal」の横に表示するプログラムです。
<body>
<div id="app">
<p><input type="text" v-model.lazy="colors[1].yellow" ></p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
</body>
v-modelにlazy修飾子をつけて、一文字づつの変更ではなく、入力確定のみを検出できるようにしています。
ブラウザとコンソールの表示
初期値はv-modelで指定した値が表示されます。
コンソールには何も表示されていません。
↓ 入力値を「黄色」にします。
コンソールに新しい値が「newVal」の下に表示され、元々の古い値が「oldVal」の下に表示されます。
watchが正常に機能していることがわかります。
immediateの使い方|初回ロード時も処理を実行する
immediateオプションを使うと、変更があった場合のみでなく、ページが読み込まれた時に処理を行うことができます。
immediate: true
を設置する。
書き方
immediateを使った書き方は以下のようになります。
watch:{
監視するプロパティ名: {
handler( 新しい値を入れる変数, 古い値を入れる変数 ){
処理
},
immediate: true
}
}
deepオプションとの併用
以下のようにdeepオプションと併用することもできます。
watch:{
監視するプロパティ名: {
handler( 新しい値を入れる変数, 古い値を入れる変数 ){
処理
},
deep: true,
immediate: true
}
}
immediateの使い方実例
dataの中に定義したnameオブジェクトの変更をwatchで監視する場合に、初回ロード時も「watchが起動しました」という処理を行いたい場合は、以下のようにimmediateオプションを設定します。
var app = new Vue({
el:"#app",
data:{
name: 'Jonson'
},
watch:{
name: {
handler(){
console.log( 'watchが起動しました')
},
immediate: true
}
}
})
nameプロパティの値を監視して、変更があったらコンソールに「watchが起動しました」と表示するプログラムです。
<body>
<div id="app">
<p><input type="text" v-model.lazy="name" ></p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
</body>
v-modelにlazy修飾子をつけて、一文字づつの変更ではなく、入力確定のみを検出できるようにしています。
ブラウザとコンソールの表示
初回ロードした時点でwatchに記述した処理が実行され、コンソールに処理内容が表示されているのがわかります。
参考:検証用コード
参考として、プロパティの値が、入れ子や配列の場合に、deepやimmediateを付けるとどのように機能するかをまとめたプログラムを記載しておきます。
コメントアウトを外したり、記載内容を変更するなどして色々と試してみてください。
.htmlファイル
<body>
<div id="app">
<p><input type="text" v-model.lazy="name" ></p>
<br><br>
<p><input type="text" v-model.lazy="user.name" ></p>
<br><br>
<p><input type="text" v-model.lazy="colors[1].yellow" ></p>
<br><br>
<p><input type="text" v-model.lazy="array[2]" ></p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
</body>
.jsファイル
var app = new Vue({
el:"#app",
data:{
name: 'Jonson',
user: {
name: 'Steve',
age: '28',
homeTown: '東京'
},
colors:[
{ red: 'あか' },
{ yellow: 'きいろ' },
{ green: 'みどり' }
],
array:[
'xxx',
'yyy',
'zzz'
]
},
watch:{
//引数を指定しない場合
// name(){
// console.log( 'watchが起動しました')
// },
name: {
handler( ){
console.log( 'watchが起動しました')
},
immediate: true
},
// name( newVal, oldVal ){
// console.log( 'newVal:' + newVal, 'oldVal:' + oldVal )
// },
user: {
handler( newVal, oldVal ){
console.log( 'newVal:', newVal, 'oldVal:', oldVal )
},
deep: true,
// immediate: true
},
colors: {
handler( newVal, oldVal ){
console.log( 'newVal:', newVal, 'oldVal:', oldVal )
},
deep: true,
// immediate: true
},
array: {
handler( newVal, oldVal ){
console.log( 'newVal:' + newVal, 'oldVal:' + oldVal )
},
// immediate: true
}
}
})
ブラウザの表示
ブラウザの表示は以下のようになっています。
初回ロード時のコンソールの状態
初回ロード時のコンソールの状態は以下のようになっています。