Vue現代化の使用方法(四)--Vuex
16578 ワード
コンポーネント内ではdata属性でデータを共有することができ、親子コンポーネントもpropsでデータ共有することができるが、兄弟がコンポーネント間のデータ共有であれば、Vuexを借りて、Vuexは大木の主幹に似ており、各コンポーネントは独立した枝に似ており、コンポーネント間ではVuexを通じてつながりを形成し、共有する必要があるデータをVuexに置く限り、すべての関連コンポーネントが参照にアクセスでき、コンポーネント間でデータ共有が形成されます.
npmによるインストール
npm install --save vuex
きほんこうせい
Vuexの設計構想によると、そのコア(Store)は以下の4つの部分を含む. state getters mutations actions
state
上記の例ではindexで見ることができます.jsが設定公共倉庫(store)のstate内容はindex.vueとheader.vueで共有し、this.$を呼び出すstore.state.countが参照することで、コンポーネント間でのデータ共有が可能になります.
計算プロパティにデータを配置し、
前例ではthis.$store.state.countは倉庫の中値にアクセスします.この書き方は冗長に見えますが、VuexはここでmapState関数を提供してこの問題を最適化しました.
getters
gettersはstateに対する計算属性です
mapStateと同様にgettersにもmapGettersの使い方がmapStateと似ています
mutation
mutationはVuex設定がstate値を変更できる唯一の場所であり、Vuexはここでこれらの値の変化について傍受追跡を行ったはずである.
このときページをクリックするとindexに表示されます.vueとheader.vueで参照されるcount値が変更されました
コンポーネント内でcommitを介してmutationsをトリガーする場合、文字列incrementを使用します.これには、プロジェクトが大きい場合、協力する人が多い場合、ページでmutationsを勝手に呼び出すと、何の役に立つか分からない問題が隠されています.
これにより、共通の場所でmutationsを管理し、注釈がはっきり書かれている場合、プロジェクト担当者は各mutationsの役割をはっきり知ることができます.
mapState,mapGetters,mutationsに対してもmapMutationsがある
mutationsは同期関数でなければなりません
公式解釈ではコールバック関数を使用するとstateの変化を明確に傍受できないため、mutationsに対する関数は同期関数のみであり、この問題に対してVuexはactionsを設定しているようです
actions actionsはmutationsをコミットし、dispatchによって をトリガする. actionsは非同期関数 をサポートする
私の理解では、actionsはmutationsの強化だと思いますので、実際の使用では、actionsに関連論理を追加し、stateを更新するためにmutationsをトリガーすることに慣れています.
Modules
上の例ではindexですjsのパブリックウェアハウスでstoreを用いて関連操作を行うと,プロジェクトが非常に大きい場合,すべてのコンポーネントで使用されるstateを1つのstroeに置くとこのstoreが大きすぎることが予想され,この問題を解決するためにstroeを機能的に複数の小さなstroeを分割して組み合わせて使用することができる.
実際のプロジェクトでは、サブウェアハウスを異なるファイルの下に配置して管理する可能性があります.
このような分割管理により,各サブウェアハウスを容易に組み合わせることができ,メンテナンスと使用が容易である.Vuexはサブウェアハウスを実装する際にstateをモジュール名詞で区切るだけであり,他のgetters,actionsは統合して使用されモジュールで区切られない.
前の例では、サブウェアハウスの関連stateを呼び出すには、ウェアハウス名を追加する必要があることがわかります:this.userStore.name/this.schoolStore.classroomですが、サブウェアハウスのgetters、mutations、actionsを呼び出すときにサブウェアハウス名を追加する必要がない場合は、情報が統合されます:this.rootCount/this.changeName,これによりサブウェアハウスのgetters,mutations,actionsが再名される場合があります
このとき、rootCountは重複するgettersキーワードであることがページに提示されますが、重複するmutationsとactionsに対して検出できません.このとき、ページのボタンをクリックすると、indexにいます.vueが設定するchangeNameを実行する方法は、userStoreを実行したい場合があります.jsでactionsですが、何気なくschoolStoreにいるからです.jsにも同名のactionsが宣言されており、2つのactionsが同時にトリガーされます(コンソールで検証できます).このような明確なエラーはなく、人為的な原因による無意識のエラーであり、開発過程ではメンテナンスが極めて難しく、特に複数の人が協力しているプロジェクトでは、したがって、mutationsとactionsに対して定義名詞(特にmutationsは、一般的にコンポーネント内で直接actionsをトリガするため、actionsが再名する可能性はmutationsよりも小さい)は、mutations-typeのような独立した定数リストに格納ことが推奨.js.
関連情報名を独立に宣言するほか、デフォルトgetters、mutations、actionsの値がグローバル宣言にバインドされているため、ネーミングスペースを使用してこの問題を解決することもできます.これにより、呼び出しが容易になります.
this['userStore/changeName']は使い勝手が悪いが、このような使い方はこの方法の所属を明確に指摘することができ、このような面倒な書き方を使いたくない場合は、ネーミングスペースを指定することもできる.
これによりchangeNameの呼び出しはuserStoreの下のコンテンツを直接呼び出すことができ、createNamespacedHelpersを使用してグローバルに自動的に追加することもできます.
ただし、この追加はすべてのバインド情報に対して行われるため、incrementはuserStoreというサブウェアハウスにも指定され、このサブウェアハウスがこの方法がないとエラーが報告されるため、この内容をどのように使用するかはよく考慮する必要がありますが、一緒に必要とする場合は、オブジェクトの形式で拡張情報を指定することもできます.
また、この同名のchangeNameについても、ファイル名によってそれぞれ
どのように使うかは、あなたの考え次第です.
サブウェアハウスで、メインウェアハウスのstateを取得する場合は、gettersはrootState、rootGettersというパラメータを送信できます.
サブウェアハウスの動的作成
使用しやすいサブウェアハウスを直接宣言するだけでなく、動的に生成することもできます.
このような一時的なサブウェアハウスは、サブコンポーネント間で一時的な通信に使用するのに非常に役立ち、完了を使用するとunregisterModuleを呼び出して直接削除することもできます.一時的に作成されたサブウェアハウスが静的ウェアハウスに影響を及ぼさないように、指定したサブウェアハウスで次のレベルのウェアハウスを作成することもできます.
ダイナミックウェアハウスは一時的に作成され、同名のものに遭遇すると直接上書きされます.
これらの動的に作成するサブウェアハウスを重ね合わせる場合は、preserveStateパラメータを追加できます.
倉庫の多重化
1つのコンポーネントを多重化すると
プラグイン
Vuexはカスタムプラグインをサポートし、プラグインによってステータスの変化をリスニングできます.
Inputの最適化
Vuexではmutationsのみでstateの値を変更するよう強制されます.inputのvalue値をstateの計算プロパティにバインドすると、stateに対応する値をどのように変更しますか?
このときinputの内容を変更しようとすると、コンソールがsetterを設定していないことを直接エラーで通知します.解決策はsetメソッドを設定し、actionsで対応するstateを変更することです.
公式サイトではvalueをバインドしてinputを傍受する方法についても言及しており、更新する案は、直接的ではないような気がします.
npmによるインストール
npm install --save vuex
きほんこうせい
Vuexの設計構想によると、そのコア(Store)は以下の4つの部分を含む.
state
// index.js
import Vuex from 'vuex'; // Vuex
Vue.use(Vuex); // Vuex
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
}
}); //
let vm = new Vue({
el: "#app",
store, //
render: h => h(app)
});
...
// index.vue
computed: {
count () {
return this.$store.state.count
}
}
...
// index.vue , header.vue
index.vue :{{count}}
...
// header.vue ,
header.vue count :{{count}}
...
//
index.vue :0
header.vue count :0
上記の例ではindexで見ることができます.jsが設定公共倉庫(store)のstate内容はindex.vueとheader.vueで共有し、this.$を呼び出すstore.state.countが参照することで、コンポーネント間でのデータ共有が可能になります.
計算プロパティにデータを配置し、
前例ではthis.$store.state.countは倉庫の中値にアクセスします.この書き方は冗長に見えますが、VuexはここでmapState関数を提供してこの問題を最適化しました.
// mapState, , , ,
//
import { mapState } from 'vuex';
...
//
computed: mapState({
count (state) {
return state.count;
},
fullName () {
return `${this.firstName}-${this.lastName}`;
}
}),
...
//
count: state => state.count,
fullName () {
return `${this.firstName}-${this.lastName}`;
}
...
// mapState , this.count store.sate.count
computed: mapState(['count']),
...
// , {{co}}
...mapState({
co: 'count'
})
...
// ,
computed: {
fullName () {
return `${this.firstName}-${this.lastName}`;
},
...mapState(['count'])
},
getters
gettersはstateに対する計算属性です
// index.js
const store = new Vuex.Store({
state: {
count: 0,
name: 'lin ken',
address: ' XX XX ',
},
getters: {
userInfo (state) {
return ` :${state.name}; :${state.address}`;
}
}
});
...
// index.vue
userInfo () {
return this.$store.getters.userInfo;
},
...
// index.vue
{{userInfo}}
mapStateと同様にgettersにもmapGettersの使い方がmapStateと似ています
// mapGetters
import { mapState,mapGetters } from 'vuex';
...
// getters
fullName () {
return `${this.firstName}-${this.lastName}`;
},
...mapGetters(['userInfo']),
...mapState(['count']),
...
// userInfo ,mapGetters ,
...mapGetters({
cusInfo: 'userInfo'
})
...
//
{{cusInfo}}
mutation
mutationはVuex設定がstate値を変更できる唯一の場所であり、Vuexはここでこれらの値の変化について傍受追跡を行ったはずである.
// index.js
mutations: {
increment (state) {
state.count++
}
}
...
// index.vue
index.vue :{{co}}
...
// index.vue methods
addCount () {
this.$store.commit('increment'); // commit mutations increment
}
このときページをクリックするとindexに表示されます.vueとheader.vueで参照されるcount値が変更されました
// commit mutations ,
// index.js
increment (state, payload) {
state.count += payload.addNum;
}
...
// index.vue
addCount () {
this.$store.commit('increment', {addNum:100}); // mutations
}
...
// ,
// commit type , type
this.$store.commit({
type: 'increment',
addNum:100
})
コンポーネント内でcommitを介してmutationsをトリガーする場合、文字列incrementを使用します.これには、プロジェクトが大きい場合、協力する人が多い場合、ページでmutationsを勝手に呼び出すと、何の役に立つか分からない問題が隠されています.
// mutation-types.js
export const INCREMENT = 'INCREMENT'; // index.js count mutations
...
// index.js
import { INCREMENT } from './mutation-types';
...
// store mutations
mutations: {
[INCREMENT] (state, payload) {
state.count += payload.addNum;
}
}
...
// index.vue mutation-types.js,
addCount () {
this.$store.commit({
type: INCREMENT,
addNum:100
})
}
これにより、共通の場所でmutationsを管理し、注釈がはっきり書かれている場合、プロジェクト担当者は各mutationsの役割をはっきり知ることができます.
mapState,mapGetters,mutationsに対してもmapMutationsがある
import { mapState, mapGetters, mapMutations } from 'vuex';
...
// methods ,mapMutations methods
// , this.INCREMENT();
methods: {
...mapMutations([INCREMENT]),
addCount () {
this.INCREMENT({addNum: 100});
}
mutationsは同期関数でなければなりません
公式解釈ではコールバック関数を使用するとstateの変化を明確に傍受できないため、mutationsに対する関数は同期関数のみであり、この問題に対してVuexはactionsを設定しているようです
actions
// index.js
// context , context.commit mutation, context.state context.getters state getters
actions: {
increment (context, payload) {
context.commit(INCREMENT, payload);
}
}
...
// ,
// state,getters
increment ({commit}, payload) {
commit(INCREMENT, payload);
}
...
// index.vue ,actionst dispatch
addCount () {
this.$store.dispatch('increment', {addNum: 100});
}
...
// commit ,
this.$store.dispatch({type: 'increment', addNum: 100});
...
// mapActions
// mapActions
import { mapState, mapGetters, mapMutations, mapActions } from 'vuex';
...
// methods
...mapActions(['increment']),
addCount () {
this.increment({addNum: 100});
}
私の理解では、actionsはmutationsの強化だと思いますので、実際の使用では、actionsに関連論理を追加し、stateを更新するためにmutationsをトリガーすることに慣れています.
Modules
上の例ではindexですjsのパブリックウェアハウスでstoreを用いて関連操作を行うと,プロジェクトが非常に大きい場合,すべてのコンポーネントで使用されるstateを1つのstroeに置くとこのstoreが大きすぎることが予想され,この問題を解決するためにstroeを機能的に複数の小さなstroeを分割して組み合わせて使用することができる.
// index.js, userStore schoolStroe
const userStore = {
state: {
name: 'lin ken from userStore',
address: ' XX XX ',
}
};
const schoolStore = {
state: {
department: ' ',
classroom: 'C509'
}
};
// Vuex.Store modules , modules
const store = new Vuex.Store({
modules: {
userStore,
schoolStore
},
state: {
count: 0,
},
getters: {
userInfo (state) {
return ` :${state.name}; :${state.address}`;
}
},
mutations: {
[INCREMENT] (state, payload) {
state.count += payload.addNum;
}
},
actions: {
increment ({commit}, payload) {
commit(INCREMENT, payload);
}
}
});
...
// index.vue ,
userStoreInfo () {
return ` :${this.userStore.name}; :${this.userStore.address}`;
},
schoolStoreInfo () {
return ` :${this.schoolStore.department}; :${this.schoolStore.classroom}`;
},
...mapState(['count', 'schoolStore', 'userStore'])
実際のプロジェクトでは、サブウェアハウスを異なるファイルの下に配置して管理する可能性があります.
// userStore.js schoolStore.js
// userStore.js
export const userStore = {
state: {
name: 'lin ken from userStore',
address: ' XX XX ',
}
};
...
// schoolStore.js
export const schoolStore = {
state: {
department: ' ',
classroom: 'C509'
}
};
...
// index.js
import {userStore} from './userStore';
import {schoolStore} from './schoolStore';
このような分割管理により,各サブウェアハウスを容易に組み合わせることができ,メンテナンスと使用が容易である.Vuexはサブウェアハウスを実装する際にstateをモジュール名詞で区切るだけであり,他のgetters,actionsは統合して使用されモジュールで区切られない.
// userStore.js
export const userStore = {
state: {
name: 'lin ken from userStore',
address: ' XX XX ',
},
getters: {
rootCount (state, getters, rootState) {
return rootState.count;
}
},
mutations: {
changeName (state, payload) {
state.name = payload.name;
}
},
actions: {
changeName ({commit}, payload) {
commit('changeName', payload);
}
}
};
...
// index.vue
...mapGetters(['rootCount']),
userStoreInfo () {
return ` :${this.userStore.name}; :${this.userStore.address};from rootState:${this.rootCount}`;
}
...
// index.vue
...mapActions(['increment', 'changeName']),
addCount () {
this.changeName({name: 'Rede'});
this.increment({addNum: 100});
}
前の例では、サブウェアハウスの関連stateを呼び出すには、ウェアハウス名を追加する必要があることがわかります:this.userStore.name/this.schoolStore.classroomですが、サブウェアハウスのgetters、mutations、actionsを呼び出すときにサブウェアハウス名を追加する必要がない場合は、情報が統合されます:this.rootCount/this.changeName,これによりサブウェアハウスのgetters,mutations,actionsが再名される場合があります
// schoolStore.js
export const schoolStore = {
state: {
department: ' ',
classroom: 'C509'
},
getters: {
rootCount (state) {
return state.classroom;
}
},
mutations: {
changeName (state, payload) {
state.department = payload.name;
}
},
actions: {
changeName ({commit}, payload) {
console.log('school store');
commit('changeName', payload);
}
}
};
...
// userStore.js
actions: {
changeName ({commit}, payload) {
console.log('user store');
commit('changeName', payload);
}
}
このとき、rootCountは重複するgettersキーワードであることがページに提示されますが、重複するmutationsとactionsに対して検出できません.このとき、ページのボタンをクリックすると、indexにいます.vueが設定するchangeNameを実行する方法は、userStoreを実行したい場合があります.jsでactionsですが、何気なくschoolStoreにいるからです.jsにも同名のactionsが宣言されており、2つのactionsが同時にトリガーされます(コンソールで検証できます).このような明確なエラーはなく、人為的な原因による無意識のエラーであり、開発過程ではメンテナンスが極めて難しく、特に複数の人が協力しているプロジェクトでは、したがって、mutationsとactionsに対して定義名詞(特にmutationsは、一般的にコンポーネント内で直接actionsをトリガするため、actionsが再名する可能性はmutationsよりも小さい)は、mutations-typeのような独立した定数リストに格納ことが推奨.js.
関連情報名を独立に宣言するほか、デフォルトgetters、mutations、actionsの値がグローバル宣言にバインドされているため、ネーミングスペースを使用してこの問題を解決することもできます.これにより、呼び出しが容易になります.
// userStore.js namespced true,
export const userStore = {
namespaced: true,
...
// index.vue
// this.rootCount userStore rootCount
...mapGetters({
'rootCount': 'userStore/rootCount'
}),
...
// ,
...mapActions(['increment', 'userStore/changeName']),
addCount () {
this['userStore/changeName']({name: 'Rede'});
this.increment({addNum: 100});
}
this['userStore/changeName']は使い勝手が悪いが、このような使い方はこの方法の所属を明確に指摘することができ、このような面倒な書き方を使いたくない場合は、ネーミングスペースを指定することもできる.
// userStore
...mapActions('userStore/', ['increment', 'changeName']),
addCount () {
this.changeName({name: 'Rede'});
this.increment({addNum: 100});
}
これによりchangeNameの呼び出しはuserStoreの下のコンテンツを直接呼び出すことができ、createNamespacedHelpersを使用してグローバルに自動的に追加することもできます.
import { mapState, mapGetters, mapMutations } from 'vuex';
import { createNamespacedHelpers } from 'vuex';
const { mapActions } = createNamespacedHelpers('userStore/');
...
// ,
...mapActions(['increment', 'changeName']),
addCount () {
this.changeName({name: 'Rede'});
this.increment({addNum: 100});
}
ただし、この追加はすべてのバインド情報に対して行われるため、incrementはuserStoreというサブウェアハウスにも指定され、このサブウェアハウスがこの方法がないとエラーが報告されるため、この内容をどのように使用するかはよく考慮する必要がありますが、一緒に必要とする場合は、オブジェクトの形式で拡張情報を指定することもできます.
//
import { mapState, mapGetters, mapMutations, mapActions } from 'vuex';
...
//
// , ,
...mapActions({
increment: 'increment',
changeName: 'userStore/changeName'
}),
addCount () {
this.changeName({name: 'Rede'});
this.increment({addNum: 100});
}
また、この同名のchangeNameについても、ファイル名によってそれぞれ
USER_CHANGENAME,
SCHOOL_CHANGENAME
どのように使うかは、あなたの考え次第です.
サブウェアハウスで、メインウェアハウスのstateを取得する場合は、gettersはrootState、rootGettersというパラメータを送信できます.
// getters ,rootState ,rootGetters
rootCount (state, getters, rootState, rootGetters)
// actions , ,
changeName ({commit, rootState, rootGetters}, payload)
サブウェアハウスの動的作成
使用しやすいサブウェアハウスを直接宣言するだけでなく、動的に生成することもできます.
// index.vue registerModule
mounted () {
this.$store.registerModule('myModule', {
state: {
info: 'from new module'
}
});
},
...
//
addCount () {
this.changeName({name: 'Rede'});
this.increment({addNum: 100});
this.info = this.$store.state.myModule.info;
this.$store.unregisterModule('myModule');
console.log(this.$store.state.myModule);
}
このような一時的なサブウェアハウスは、サブコンポーネント間で一時的な通信に使用するのに非常に役立ち、完了を使用するとunregisterModuleを呼び出して直接削除することもできます.一時的に作成されたサブウェアハウスが静的ウェアハウスに影響を及ぼさないように、指定したサブウェアハウスで次のレベルのウェアハウスを作成することもできます.
mounted () {
this.$store.registerModule('nested', {
state: {
info: 'nested'
}
});
this.$store.registerModule(['nested', 'myModule'], {
state: {
info: 'from new module'
}
});
},
...
//
console.log(this.$store.state.nested.info);
console.log(this.$store.state.nested.myModule.info);
ダイナミックウェアハウスは一時的に作成され、同名のものに遭遇すると直接上書きされます.
// myModule ,myModule state name
mounted () {
this.$store.registerModule('nested', {
state: {
info: 'nested'
}
});
this.$store.registerModule(['nested', 'myModule'], {
state: {
info: 'from new module'
}
});
this.$store.registerModule(['nested', 'myModule'], {
state: {
name: 'myModule'
}
});
},
これらの動的に作成するサブウェアハウスを重ね合わせる場合は、preserveStateパラメータを追加できます.
// myModule state info name
this.$store.registerModule(['nested', 'myModule'], {
state: {
info: 'from new module'
}
});
this.$store.registerModule(['nested', 'myModule'], {
state: {
name: 'myModule'
}
}, { preserveState: true });
倉庫の多重化
1つのコンポーネントを多重化すると
プラグイン
Vuexはカスタムプラグインをサポートし、プラグインによってステータスの変化をリスニングできます.
// index.js
// store Vuex
// subscribe mutations
// subscribeAction action
const myPlugin = store => {
// store
store.subscribe((mutation, state) => {
console.log('from myPlugin');
console.log(mutation);
});
store.subscribeAction((action, state) => {
console.log('from myPlugin');
console.log(action);
})
};
...
// plugins
const store = new Vuex.Store({
...
plugins: [myPlugin]
});
Inputの最適化
Vuexではmutationsのみでstateの値を変更するよう強制されます.inputのvalue値をstateの計算プロパティにバインドすると、stateに対応する値をどのように変更しますか?
// index.vue
...
userStoreInfo () {
return `${this.userStore.name}`;
},
このときinputの内容を変更しようとすると、コンソールがsetterを設定していないことを直接エラーで通知します.解決策はsetメソッドを設定し、actionsで対応するstateを変更することです.
userStoreInfo: {
get () {
return `${this.userStore.name}`;
},
set (value) {
this.changeName({name: value});
}
},
公式サイトではvalueをバインドしてinputを傍受する方法についても言及しており、更新する案は、直接的ではないような気がします.