問題
Vue.jsのComponentの勉強のため、カウンターを作っていたときのことです。
1つのボタンを押したら、他のボタンのカウントも増えてしまう問題が発生。
環境
- Vue.js v2.6.11(CDN)
コード
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Components</title>
<link rel="stylesheet" href="css/styles.css">
</head>
<body>
<div id="app">
<button-counter message="Counter A"></button-counter>
<button-counter message="Counter B"></button-counter>
<button-counter message="Counter C"></button-counter>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
<script src="js/main.js"></script>
</body>
</html>
(function () {
'use strict';
const counter = {
count: 0
};
const countComponent = Vue.extend({
template: '<button @click="countUp">{{ message }}: {{ count }}</button>',
props: {
message: {
type: String,
},
},
data: function () {
return counter;
},
methods: {
countUp: function () {
this.count++;
},
},
});
const vm = new Vue({
el: '#app',
components: {
'button-counter': countComponent,
},
});
})();
原因
リファレンスを読むと、
コンポーネントの data オプションは関数でなければなりません。各インスタンスが返されるデータオブジェクトの独立したコピーを保持できるためです:
という記述がありました。
参照:【Vue.js ガイド】data は関数でなければなりません
それぞれのカウントボタンインスタンスに返される値を独立させるために、dataオプションを関数にしなければならないのですね。
解決
リファレンスに記述の仕方がちゃんと書いてありました。
data: function () {
return {
count: 0
}
}
Vue にこのルールがない場合、ボタンを1つクリックすると、以下のようにすべての他のインスタンスのデータに影響します:
丁寧に今回の事象の例も提示してありました。
修正後はこうなりました。
``````js:js/main.js
(function () {
'use strict';
// 削除
// const counter = {
// count: 0
// };
const countComponent = Vue.extend({
template: '<button @click="countUp">{{ message }}: {{ count }}</button>',
props: {
message: {
type: String,
},
},
// 変更-----
data: function () {
return {
count: 0,
};
},
// -----
methods: {
countUp: function () {
this.count++;
},
},
});
const vm = new Vue({
el: '#app',
components: {
'button-counter': countComponent,
},
});
})();
ちゃんとそれぞれのボタンの押下数をカウントしてくれるようになりました。
反省
ちなみにcountComponentの中でdataオプションを関数にしないままcountを定義しようとすると、Vue.jsの方でReferenceError: count is not defined
と、エラーになることを教えてくれました。
そもそも、countは別で定義するのではなく、処理をしているインスタンス内で設定すればよかったのですね。