Vuexに来ているの?
公開: 2022-03-10Vuexは、Vueアプリケーションの状態管理のためのソリューションです。 次のバージョンであるVuex4は、正式にリリースされる前に最終ステップを通過しています。 このリリースはVue3との完全な互換性をもたらしますが、新しい機能は追加されません。 Vuexは常に強力なソリューションであり、Vueの状態管理のための多くの開発者にとって最初の選択肢でしたが、一部の開発者は、より多くのワークフローの問題が解決されることを望んでいました。 しかし、Vuex 4が出てきたばかりなのに、Kia King Ishii(Vueコアチームメンバー)がVuex 5の計画について話しているので、私はそれをあなたと共有しなければならなかったのを見てとても興奮していますすべて。 Vuex 5の計画はまだ確定していないため、Vuex 5がリリースされる前に変更される可能性がありますが、最終的にこの記事の内容とほぼ同じになる場合は、開発者のエクスペリエンスが大幅に向上するはずです。
Vue 3とそのコンポジションAPIの登場により、人々は手作りのシンプルな代替案を検討してきました。 たとえば、 Vuexは、共有状態ストアを作成するためのprovide/inject
とともにコンポジションAPIを使用するための、比較的シンプルでありながら柔軟で堅牢なパターンを示しています。 ただし、Gaborが彼の記事で述べているように、これ(および他の代替手段)は、コードに直接関係しないもの(コミュニティのサポート、ドキュメント、規則、優れたNuxt統合、開発者)がすべて欠けているため、小規模なアプリケーションでのみ使用する必要があります。ツール。
最後の1つは、常に私にとって最大の問題の1つです。 Vue devtoolsブラウザー拡張機能は、常にVueアプリをデバッグおよび開発するためのすばらしいツールであり、「タイムトラベル」でVuexインスペクターを失うことは、重要なアプリケーションをデバッグするためのかなり大きな損失になります。

ありがたいことに、Vuex 5を使用すると、ケーキを食べて食べることができるようになります。 これらのコンポジションAPIの代替手段のように機能しますが、公式の状態管理ライブラリを使用することのすべての利点を維持します。 それでは、何が変わるのかを見てみましょう。
ストアの定義
Vuexストアで何かを行う前に、Vuexストアを定義する必要があります。 Vuex 4では、ストア定義は次のようになります。
import { createStore } from 'vuex' export const counterStore = createStore({ state: { count: 0 }, getters: { double (state) { return state.count * 2 } }, mutations: { increment (state) { state.count++ } }, actions: { increment (context) { context.commit('increment') } } })
各ストアには4つの部分があります。 state
はデータを格納し、 getters
は計算された状態を提供し、 mutations
テーションは状態をミューテーションするために使用され、 actions
はストアに関連するすべてを実行するためにストアの外部から呼び出されるメソッドです。 通常、この例が示すように、アクションは単にミューテーションをコミットするだけではありません。 代わりに、ミューテーションは同期する必要があるため、非同期タスクを実行するために使用されます。そうでない場合は、より複雑な機能やマルチステップ機能を実装するだけです。 アクションは、それ自体で状態を変更することもできません。 ミューテーターを使用する必要があります。 では、Vuex 5はどのように見えますか?
import { defineStore } from 'vuex' export const counterStore = defineStore({ name: 'counter', state() { return { count: 0 } }, getters: { double () { return this.count * 2 } }, actions: { increment () { this.count++ } } })
ここで注意すべきいくつかの変更があります。 まず、 createStore
の代わりにdefineStore
を使用します。 この違いはごくわずかですが、セマンティック上の理由から存在します。これについては後で説明します。 次に、以前は必要なかった店舗のname
を指定する必要があります。 以前は、モジュールには独自の名前が付けられていましたが、モジュール自体によって提供されたものではありませんでした。 それらは、それらを追加した親ストアによって割り当てられたプロパティ名にすぎませんでした。 現在、モジュールはありません。 代わりに、各モジュールは個別のストアになり、名前が付けられます。 この名前は、後で説明するVuexレジストリで使用されます。
その後、stateを単に初期状態に設定するのではなく、初期state
を返す関数にする必要があります。 これは、コンポーネントのdata
オプションに似ています。 Vuex 4で行った方法と非常によく似たgetters
を作成しますが、各ゲッターのパラメーターとしてstate
を使用する代わりに、 this
を使用して状態を取得できます。 同様に、 actions
はcontext
オブジェクトが渡されることを心配する必要はありません。アクションは、 this
を使用してすべてにアクセスできます。 最後に、 mutations
はありません。 代わりに、ミューテーションはactions
と組み合わされます。 Kiaは、突然変異が単純なセッターになり、無意味に冗長になることが多すぎるため、それらを削除したと述べました。 彼は店の外から直接状態を変化させることが「大丈夫」であるかどうかについては言及しませんでしたが、アクションから直接状態を変化させることは間違いなく許可され、奨励されており、フラックスパターンは状態の直接変化に眉をひそめます。
注:コンポーネントを作成するためのオプションAPIよりもコンポジションAPIを好む方は、コンポジションAPIを使用するのと同様の方法でストアを作成する方法もあることを知っていただければ幸いです。
import { ref, computed } from 'vue' import { defineStore } from 'vuex' export const counterStore = defineStore('counter', { const count = ref(0) const double = computed(() => count.value * 2) function increment () { count.value++ } return { count, double, increment } })
上に示したように、名前はdefineStore
の最初の引数として渡されます。 残りは、コンポーネントの合成関数のように見えます。 これにより、オプションAPIを使用した前の例とまったく同じ結果が得られます。
ストアをインスタンス化する
Vuex 4では、Vuex 3から状況が変わりましたが、手に負えなくなるのを防ぐために、v4だけを見ていきます。 v4では、 createStore
を呼び出したときに、すでにインスタンス化されています。 その後、 app.use
を介して、または直接、アプリで使用できます。
import { createApp } from 'vue' import App from './App.vue' // Your root component import store from './store' // The store definition from earlier const app = createApp(App) app.use(store) app.mount('#app') // Now all your components can access it via `this.$store` // Or you can use in composition components with `useStore()` // ----------------------------------------------- // Or use directly... this is generally discouraged import store from './store' store.state.count // -> 0 store.commit('increment') store.dispatch('increment') store.getters.double // -> 4
これは、Vuex5がv4よりも少し複雑にすることの1つです。 各アプリはVuexの個別のインスタンスを取得できるようになりました。これにより、各アプリは、データを共有せずに同じストアの個別のインスタンスを持つことができます。 アプリ間でストアのインスタンスを共有する場合は、Vuexのインスタンスを共有できます。
import { createApp } from 'vue' import { createVuex } from 'vuex' import App from './App.vue' // Your root component const app = createApp(App) const vuex = createVuex() // create instance of Vuex app.use(vuex) // use the instance app.mount('#app')
これで、すべてのコンポーネントがVuexインスタンスにアクセスできるようになります。 ストア定義を直接指定する代わりに、それらを使用するコンポーネントにインポートし、Vuexインスタンスを使用してそれらをインスタンス化して登録します。

import { defineComponent } from 'vue' import store from './store' export default defineComponent({ name: 'App', computed: { counter () { return this.$vuex.store(store) } } })
$vuex.store
を呼び出すと、ストアがインスタンス化され、Vuexインスタンスに登録されます。 その時点から、そのストアで$vuex.store
を使用すると、再度インスタンス化するのではなく、すでにインスタンス化されているストアが返されます。 createVuex()
によって作成されたVuexのインスタンスでstore
メソッドを直接呼び出すことができます。
これで、 this.counter
を介してそのコンポーネントからストアにアクセスできます。 コンポーネントにコンポジションAPIを使用している場合は、 this.$vuex.store
の代わりにuseStore
を使用できます。
import { defineComponent } from 'vue' import { useStore } from 'vuex' // import useStore import store from './store' export default defineComponent({ setup () { const counter = useStore(store) return { counter } } })
ストアをコンポーネントに直接インポートしてそこでインスタンス化することには、賛否両論があります。 これにより、コード分割が可能になり、必要な場所にのみストアを遅延ロードできますが、親によって注入されるのではなく、直接の依存関係になります(使用するたびにインポートする必要があることは言うまでもありません)。 依存性注入を使用してアプリ全体に提供したい場合、特にコード分割が役に立たないアプリのルートで使用されることがわかっている場合は、 provide
:を使用できます。
import { createApp } from 'vue' import { createVuex } from 'vuex' import App from './App.vue' import store from './store' const app = createApp(App) const vuex = createVuex() app.use(vuex) app.provide('store', store) // provide the store to all components app.mount('#app')
そして、それを使用するコンポーネントに注入することができます。
import { defineComponent } from 'vue' export default defineComponent({ name: 'App', inject: ['store'] }) // Or with Composition API import { defineComponent, inject } from 'vue' export default defineComponent({ setup () { const store = inject('store') return { store } } })
私はこの余分な冗長性に興奮していませんが、それはより明確でより柔軟であり、私はそれが好きです。 このタイプのコードは通常、プロジェクトの開始時にすぐに作成され、その後は気になりませんが、新しいストアを提供するか、使用するたびにインポートする必要がありますが、コードモジュールのインポートまたは挿入は、一般的に他のものと連携する方法であるため、Vuexは、人々がすでに作業する傾向に沿って動作するようになっています。
ストアの使用
コンポジションAPIを使用するコンポーネントと同じ方法でストアを定義する柔軟性と新しい方法のファンであることとは別に、他のすべてよりもワクワクすることが1つあります。それは、ストアの使用方法です。 Vuex4でストアを使用すると次のようになります。
store.state.count // Access State store.getters.double // Access Getters store.commit('increment') // Mutate State store.dispatch('increment') // Run Actions
State
、 getters
、 mutations
テーション、およびactions
はすべて、さまざまなプロパティまたはメソッドを介してさまざまな方法で処理されます。 これには、私が以前に賞賛した明示性の利点がありますが、この明示性は実際には何も得られません。 また、このAPIは、名前空間モジュールを使用している場合にのみ使用が難しくなります。 比較すると、Vuex 5は、通常の期待どおりに機能するように見えます。
store.count // Access State store.double // Access Getters (transparent) store.increment() // Run actions // No Mutators
状態、ゲッター、アクションなどすべてがストアのルートで直接利用できるため、冗長性を大幅に減らして簡単に使用でき、オプションAPIまたは書き込みにmapMutations
、 mapGetters
、 mapState
、 mapActions
を使用する必要がほとんどなくなります。構成API用の追加のcomputed
ステートメントまたは単純な関数。 これにより、Vuexストアは、自分で作成する通常のストアと同じように見え、動作しますが、プラグイン、デバッグツール、公式ドキュメントなどのすべての利点が得られます。
作曲店
今日見るVuex5の最後の側面は、構成可能性です。 Vuex 5には、単一のストアからすべてアクセスできる名前空間モジュールはありません。 これらの各モジュールは、完全に別個のストアに分割されます。 これは、コンポーネントを処理するのに十分簡単です。必要なストアをインポートし、起動して使用するだけです。 しかし、あるストアが別のストアとやり取りしたい場合はどうでしょうか。 v4では、名前空間がすべてを複雑にするため、 commit
およびdispatch
呼び出しで名前空間を使用し、 rootGetters
とrootState
を使用してから、ゲッターと状態にアクセスする名前空間に到達する必要があります。 Vuex5での動作は次のとおりです。
// store/greeter.js import { defineStore } from 'vuex' export default defineStore({ name: 'greeter', state () { return { greeting: 'Hello' } } }) // store/counter.js import { defineStore } from 'vuex' import greeterStore from './greeter' // Import the store you want to interact with export default defineStore({ name: 'counter', // Then `use` the store use () { return { greeter: greeterStore } }, state () { return { count: 0 } }, getters: { greetingCount () { return `${this.greeter.greeting} ${this.count}' // access it from this.greeter } } })
v5では、使用したいストアをインポートしてから、 use
に登録すると、ストア全体で、指定したプロパティ名でアクセスできるようになります。 ストア定義のコンポジションAPIバリエーションを使用している場合は、さらに簡単になります。
// store/counter.js import { ref, computed } from 'vue' import { defineStore } from 'vuex' import greeterStore from './greeter' // Import the store you want to interact with export default defineStore('counter', ({use}) => { // `use` is passed in to function const greeter = use(greeterStore) // use `use` and now you have full access const count = 0 const greetingCount = computed(() => { return `${greeter.greeting} ${this.count}` // access it like any other variable }) return { count, greetingCount } })
名前空間付きのモジュールはもうありません。 各店舗は別々であり、別々に使用されます。 useをuse
して、ストアを別のストア内で使用できるようにして、それらを作成できます。 どちらの例でも、 use
は基本的に以前のvuex.store
とまったく同じメカニズムであり、Vuexの正しいインスタンスでストアをインスタンス化することを保証します。
TypeScriptサポート
TypeScriptユーザーにとって、Vuex 5の最大の特徴の1つは、単純化によってすべてに型を追加することが簡単になったということです。 古いバージョンのVuexがほぼ不可能にしていた抽象化レイヤーにより、現在、Vuex 4を使用すると、型を使用する能力が向上しましたが、v5では、適切な量の型サポートを取得するには、依然として手作業が多すぎます。 、希望と期待どおりに、タイプをインラインに配置できます。
結論
Vuex 5は、私(そしておそらく他の多くの人)が望んでいたものとほぼ同じように見えますが、すぐには実現できないと感じています。 Vuexのほとんどを簡素化し、関連する精神的なオーバーヘッドの一部を取り除き、柔軟性を追加する場合にのみ、より複雑または冗長になります。 これらの変更についてどう思うか、代わりにまたは追加でどのような変更を加える可能性があるかについて、以下にコメントを残してください。 または、ソースに直接アクセスし、RFC(Request for Comments)をリストに追加して、コアチームの考えを確認します。