Edited at

vue.jsでバインディング済みのオブジェクトや配列を書き換えるときの注意点

More than 3 years have passed since last update.


はじめに

vue.jsでバインディング済みオブジェクトや配列を書き換える際に,(ビューに反映させるために)状況によってはvue.jsが用意する拡張メソッドを利用する必要があります。

結論としては「Object.$addなりArray.$setなり使えよ」ということになるのですが,どういうケースでこれらのメソッドを呼ぶ必要があるのかが(わたしのような)初心者にはすぐに理解できなかったので,サンプルを交えつつ紹介したいと思います。


前提コード

まず次のようなコードを考えます。

<script src="//cdnjs.cloudflare.com/ajax/libs/vue/0.11.0/vue.min.js"></script>

<div id="container">
<div>
0: {{array[0].name}}, {{array[0].prop}}
</div>
<div v-repeat="array" v-show="$index > 0">
{{$index}}: {{name}}, {{prop}}
</div>
</div>
<script>
var app = new Vue({
el: "#container",
ready: function() {
var self = this
setTimeout(function() {
self.array[0].prop = true
self.array[1].prop = true
self.array[2].prop = true
}, 1000)
},
data: {
array: [
{ name: "a" },
{ name: "b" },
{ name: "c" }
]
}
})
</script>

ページがレンダリングされてから1秒後にapp.$data.arrayが書き換えられ,最終的には次のような結果になることを期待したコードです。

0: a, true

1: b, true
2: c, true

しかしながら,実際に上記のコードを実行してみると,次のようにpropへの変更が反映されません。

0: a,

1: b,
2: c,


プロパティを追加するために$addを呼ぶ

配列の各要素となるオブジェクトに対して,元々存在しなかったpropというプロパティを追加している点に注目してください。このようなケースでは,ViewModelにオブジェクトのプロパティが追加されたことを知らせるために,$addを呼んであげる必要があります。

サンプルコードのsetTimeout呼び出し部分を次のコードに置き換えます。

setTimeout(function() {

self.array[0].$add("prop", true)
self.array[1].$add("prop", true)
self.array[2].$add("prop", true)
}, 1000)

結果は次のようになりました。

0: a,

1: b, true
2: c, true

v-repeatを使わずに直接参照している部分がうまく動いていませんね。どのようにすれば思い通りの結果が得られるでしょうか?


配列の要素を書き換えるために$setを呼ぶ

先ほどは直接配列の要素を参照している部分がうまく動きませんでした。どうやら,配列の特定の要素が書き換えられていることをvue.jsに教えてあげる必要がありそうです。

サンプルコードのsetTimeout呼び出し部分を,更に次のように書き換えます。

setTimeout(function() {

var f = function(i) {
var o = self.array[i]
o.$add("prop", true)
self.array.$set(i, o)
}
f(0)
f(1)
f(2)
}, 1000)

結果は次のようになりました。

0: a, true

1: b, true
2: c, true

うまく動きました!


おわりに

今回のケースでは,$add$setの両方を呼ばないと思い通りの結果を得ることができませんでした。

vue.jsでバインディング済みの配列やオブジェクトの書き換えをする場合には,これらの拡張メソッドを呼ぶ必要がないかどうか,注意を払う必要がありそうです。拡張メソッドには他の種類もあるので,詳しくは関連リンクの本家ドキュメントを読んでみてください。

動作確認環境は下記の通りです。

使用ライブラリ・ソフト
バージョン

vue.js
0.11.0

Google Chrome
38.0.2125.122

Mozilla Firefox
33.1.1

どうしてこんな面倒なメソッド呼び出しが必要なのか,ということについては,関連リンクのてっく煮ブログさんに書かれているので引用します。


こんな面倒なことになっているのも、Vue.js が ECMAScript 5 の世界で頑張っているからです。

ECMAScript に提案されている Object.observe() が使える世界になれば、Object や Array の置き換えも不要になるし、Vue.js の設計もシンプルになることでしょう。



関連リンク