Edited at

[Vue.js] なぜpropsのdefault値にObjectやArrayを指定する際にfactory関数にする必要があるのか

あなたがこの記事を見ているということは、コンソールに以下のワーニングが出ていることでしょう。


WARN

[Vue warn]: Invalid default value for prop "XXX": Props with type Object/Array must use a factory function to return the default value.

このワーニングはVueコンポーネントのpropsに関するものです。

「propsのdefaultの値にObjectやArrayを使用したい場合、ファクトリー関数を指定しろ」

という内容のワーニングです。


ワーニングを解消する

以下のようにコードを修正することでワーニングを解消できます。


修正前

props: {

obj: {
type : Object,
require : false,
'default': { count: 0 } // Objectをそのまま指定している
},
}


修正後

props: {

obj: {
type : Object,
require : false,
'default': () => ({ count: 0 }) // Objectを生成する関数を指定する
},
}


なぜ関数を指定する必要があるのか

なぜ、ObjectやArrayをデフォルト値としたい場合、関数を指定する必要があるのでしょうか。


ワーニングを無視すると何が起こるか

まず、ワーニングを無視した場合に何が起こるのか試してみます。


AppCounter.vue

<template>

<div class="content">
<span>{{ obj.count }}</span>
<button @click="obj.count++;">+1</button>
</div>
</template>

<script>
export default {
props: {
obj: {
type: Object,
require: false,
'default': { count: 0 } // ワーニングを無視して、Objectをそのまま指定している
}
}
};
</script>


「+1」ボタンのクリックでカウンターをインクリメントする、Vueコンポーネントです。


App.vue

<template>

<div id="app">
<app-counter />
<app-counter />
</div>
</template>

<script>
import AppCounter from "./components/AppCounter";

export default {
name: "App",
components: {
AppCounter
}
};
</script>


そのカウンターを2つ配置して動かしてみると、以下のような結果になります。

counter01.gif

どちらのボタンをクリックしても、2つのカウンターがインクリメントされています。

↓のリンクから実際に触っていただけます。

Edit Vue Template


オブジェクトや配列は値がコピーされず、参照が共有される

propsのデフォルト値に指定したオブジェクトや配列は、値がコピーされることなく参照が共有されます。

そのため、複数のvueインスタンスが同一のObjecを参照し、同一のObjectに変更を加えてしまう状況が発生します。

2つのカウンターがどちらのボタンの操作でもインクリメントされるのは、このためです。

ワーニングを出すのではなく、フレームワーク側でObjectや配列を自動でディープコピーしてくれればいいのでは?と考えることもできるのですが、開発者側で

ファクトリー関数を指定できるようにすることで、ディープコピーにまつわるリスクを抑えつつ、実装の選択肢を広げ自由度を上げる目的があるようです。


参考

https://github.com/vuejs/vue/issues/1032