JavaScript
TypeScript
Vue.js
Vuex
Vue3
2
どのような問題がありますか?

投稿日

更新日

Vuexはもう古い!?これからの状態管理はPiniaを使おう

はじめに

煽り気味のタイトルですみません。Vuexは使わない方がいいという話ではなく、別の有力な選択肢が出てきたというレベルの話であると最初に断っておきます。

みなさん、Piniaという状態管理ライブラリをご存知ですか?僕は昨日知りました。
調べてみるとどうやら先月Vueの公式リポジトリ入りをしており、今後さらに注目が集まるライブラリになりそうです。
* ちなみにPiniaはVuexを置き換えるものではなく、Vuexは今後も開発を継続していくようです(現在はVuex5を開発中)。

Vueの状態管理といえばVuexというイメージがありますが、TypeScriptとの相性が良くないだとか、そもそも冗長だとかで長らくVue3時代の状態管理のベストプラクティスがよくわからない状況が続いていました(?)

僕の知っている限りではVue状態管理の方法は
1. Vuex
2. provide/inject
3. 単にグローバルでリアクティブ変数を宣言する
4. Pinia

の4つですが、1の問題は前述した通りで、2、3はシンプルですがプロダクトやチームの規模によっては無秩序になりやすいという問題があります。

その点Piniaはシンプルで型安全かつパフォーマンスも良いというので、かなり有力な候補になり得るのではないでしょうか。

目次

  1. 基本的な使い方
  2. 補足
  3. 参考文献

基本的な使い方

導入は簡単
公式より

yarn add pinia
main.ts
import { createPinia } from 'pinia'

// ...

app.use(createPinia())

これだけ

ストアはdefineStoreで定義
ぱっと見そんなにVuexと変わらないので、Vuex利用者は特に説明がなくても理解できると思います。
第一引数はアプリケーション内でユニークなキー
第二引数はストアの定義となるオブジェクトで、基本はstate,getters,actionsの3つのプロパティを指定
vuexと違いmutationsが存在しないのでよりシンプルですね

stores/counter.ts
import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', {
  state: () => {
    return {
      count: 0,
    }
  },
  getters: {
    double: (state) => state.count * 2,
  },
  actions: {
    increment() {
      this.count++
    },
  }
})

使用する際は

<script lang="ts">
import { defineComponent } from 'vue'
import { useCounterStore } from '@/stores/counter'
import { storeToRefs } from 'pinia'

export default defineComponent({
  setup() {
    const counterStore = useCounterStore()
    // toRefsだとreactivityが壊れるので注意
    const { count } = storeToRefs(counterStore)

    return {
      counterStore,
      count
    }
  },
})
</script>
<template>
  <div>{{ count }}</div>
  <div>{{ counterStore.double }}</div>
  <button @click="counterStore.increment()" > increment! </button>
</template>

<script setup>記法で書くと ※ この記法を知らない方はここを参照

<script setup lang="ts">
import { useCounterStore } from '@/stores/counter'
import { storeToRefs } from 'pinia'

const counterStore = useCounterStore()

const { count } = storeToRefs(counterStore)

</script>
<template>
  <div>{{ count }}</div>
  <div>{{ counterStore.double }}</div>
  <button @click="counterStore.increment()" > increment! </button>
</template>

基本的な使用方法は以上です。
storeToRefsでリアクティブを維持したまま分割代入可能です。
gettersプロパティはcomputedと同様にリアクティブです。
おまじないが少なくて作業中に頭が混乱するのを防げそうですね。
補完もバッチリ効きます。

ちなみに、store.count++ とかstore.count = 10とかも可能です
実態はreactive()で作成したリアクティブオブジェクトのラッパーにすぎないのでこういうこともできちゃいます。
実際の運用ではstateを直接変更するのは禁止すべきかもしれませんね。

僕の場合、個人開発ではpiniaっぽいことをわざわざライブラリ無しでやっていたので、まさに自分が求めていたものが既に存在していたと知り感動しました。

補足

  • 他のstoreにアクセスしたい場合

この場合もシンプルで、単に使用箇所でストアを取得しプロパティにアクセスするだけです。

stores/counter.ts
import { useOtherStore } from './otherStore'

export const useCounterStore = defineStore('counter', {
  state: () => {
    return {
      count: 0,
    }
  },
  getters: {
    other: (state) => {
      const other = useOtherStore()
      return other.num * state.count
    }
  },
})

参考文献

- 公式サイト

ユーザー登録して、Qiitaをもっと便利に使ってみませんか。
  1. あなたにマッチした記事をお届けします
    ユーザーやタグをフォローすることで、あなたが興味を持つ技術分野の情報をまとめてキャッチアップできます
  2. 便利な情報をあとで効率的に読み返せます
    気に入った記事を「ストック」することで、あとからすぐに検索できます
ユーザー登録ログイン
__Nem__
フロントエンド Vue,TypeScript,GraphQL,猫
この記事は以下の記事からリンクされています

コメント

この記事にコメントはありません。
あなたもコメントしてみませんか :)
ユーザー登録
すでにアカウントを持っている方はログイン
記事投稿イベント開催中
GitHub Actionsの自分流の使い方をシェアしよう
~
買ってよかった技術書を紹介しよう!
~
2
どのような問題がありますか?
ユーザー登録して、Qiitaをもっと便利に使ってみませんか

この機能を利用するにはログインする必要があります。ログインするとさらに下記の機能が使えます。

  1. ユーザーやタグのフォロー機能であなたにマッチした記事をお届け
  2. ストック機能で便利な情報を後から効率的に読み返せる
ユーザー登録ログイン
ストックするカテゴリー