先日問題が発生し、すばらしいスタックコミュニティに解決策を求めました。
同じモジュールを他のモジュールにネストしましたが、このように状態を定義していました:
state: {
// some state here
}
異種のモジュールの下にネストされているように見えても、すべてのモジュールが同じ状態を共有していたのです。
state() {
return {
// state here instead
}
}
解決策は、オブジェクトリテラルとして定義するのではなく、関数の戻り状態にすることです。その理由はやや理にかなっています。これが私の質問です
状態がオブジェクトリテラルとして定義されているのに対し、オブジェクトリテラルを返す関数に対してストアの内部で何が起こっているのですか?
なぜ関数バージョンを使用しないのですか?簡単にデフォルトの選択のように見えますが、 vuex docs for modules
でも、状態をオブジェクトリテラルとして表示することを選択します。
オブジェクトリテラルを返す関数ではなく、オブジェクトリテラルとして状態が定義されている場合、ストアの内部では何が起こっていますか?
そのためには、より良い 内部で確認する :
var Store = function Store (options) {
// ...
var state = options.state; if ( state === void 0 ) state = {};
if (typeof state === 'function') {
state = state() || {};
}
上記のコードは、ご覧のとおり、state
が指定されているかどうかを確認します。そうでない場合は、空のオブジェクト({}
)を初期state
として割り当てます。
次に、state
がfunction
であったかどうかをチェックします。存在する場合は、それを実行し、返されたものをstate
に割り当てます。それがundefined
(または任意のfalsy値)を返した場合、再びstate
に空のオブジェクト{}
を割り当てます。
これがstate
をオブジェクトまたは関数として提供することの違いです。提供された場合、それが実行されます。オブジェクトが提供されている場合、が直接割り当てられます。
なぜ関数バージョンを使用しないのですか?これは簡単にデフォルトの選択のように見えますが、モジュールのvuexドキュメントでも、状態をオブジェクトリテラルとして表示することを選択します。
一般に、はい。オブジェクトのバージョンのほうが一般的です。通常、store
オブジェクト(およびそのstate
)を宣言するのは1回だけで、Vueインスタンス。
state
関数otohの使用例は Module Reuse です。
モジュールの再利用
たとえば、モジュールの複数のインスタンスを作成する必要がある場合があります。次に例を示します。
- 同じモジュールを使用する複数のストアを作成する(たとえば、runInNewContextオプションがfalseまたは 'once'の場合、SSRで ステートフルシングルトンを回避するため )。
- 同じモジュールを同じストアに複数回登録します。
別の可能性のあるケースは、Vuexモジュールを1回だけ宣言し、それを複数回使用しようとした場合です。異なる名前空間。
上記の例は似ているので、問題を説明するための(モジュールのケースの)デモを次に示します。
const personModule = {
namespaced: true,
state: {name: "name"},
mutations: {
changeName(state, data) { state.name = data }
}
}
const myStore = new Vuex.Store({
strict: true,
modules: {
aliceNamespace: personModule,
bobNamespcace: personModule
}
});
new Vue({
store: myStore,
el: '#app',
mounted() {
this.changeAlicesName("Alice");
this.changeBobsName("Bob");
},
computed: {
...Vuex.mapState('aliceNamespace', {alicesName: 'name'}),
...Vuex.mapState('bobNamespcace', {bobsName: 'name'})
},
methods: {
...Vuex.mapMutations('aliceNamespace', {changeAlicesName: 'changeName'}),
...Vuex.mapMutations('bobNamespcace', {changeBobsName: 'changeName'})
}
})
<script src="https://unpkg.com/vue"></script>
<script src="https://unpkg.com/vuex"></script>
<div id="app">
<p>Alice's name: {{ alicesName }}</p>
<hr>
<p>Bob's name: {{ bobsName }}</p>
<hr>
<button @click="changeAlicesName('Eve')">Change Alice's Name</button>
</div>
ご覧のとおり、状態を使用すると、同じオブジェクトが両方のモジュールのstate
として割り当てられます。これの影響は、モジュールを編集するときに、他のモジュールが影響を受けることです。実際には、2つの異なるモジュールである可能性がありますが、それらのstate
は、1つの同じオブジェクトにすぎません。
一方、以下の例では、state
を関数として宣言すると、モジュール宣言を何度でも自由に再利用できます。
const personModule = {
namespaced: true,
state() { // changed to a function
return {name: "name"} // changed to a function
}, // changed to a function
mutations: {
changeName(state, data) { state.name = data }
}
}
const myStore = new Vuex.Store({
strict: true,
modules: {
aliceNamespace: personModule,
bobNamespcace: personModule
}
});
new Vue({
store: myStore,
el: '#app',
mounted() {
this.changeAlicesName("Alice");
this.changeBobsName("Bob");
},
computed: {
...Vuex.mapState('aliceNamespace', {alicesName: 'name'}),
...Vuex.mapState('bobNamespcace', {bobsName: 'name'})
},
methods: {
...Vuex.mapMutations('aliceNamespace', {changeAlicesName: 'changeName'}),
...Vuex.mapMutations('bobNamespcace', {changeBobsName: 'changeName'})
}
})
<script src="https://unpkg.com/vue"></script>
<script src="https://unpkg.com/vuex"></script>
<div id="app">
<p>Alice's name: {{ alicesName }}</p>
<hr>
<p>Bob's name: {{ bobsName }}</p>
<hr>
<button @click="changeAlicesName('Eve')">Change Alice's Name</button>
</div>
state
は関数であるため、モジュールごとに異なるstate
インスタンスを生成し、期待どおりに機能します。