1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Pinia × Composition API:Vuexとの比較から得た学び

Posted at

1. はじめに

これまで状態管理ライブラリとして、Vuexを使用してきましたが、最近Piniaを使用する機会がありました。
Vuexとの違いで躓いた部分や、新たに得た学びがあったので以下にまとめます。

2. Piniaとは

Piniaは、Vueチームによって公式に推奨されている状態管理ライブラリであり、Vuexの後継と位置付けられています。Vue 3のComposition APIとの統合やTypeScript対応を前提に設計されており、以下のような特徴があります。

機能 Vuex Pinia
Stateの定義 state ref() / reactive()
Getter getters computed()
Action actions 任意の関数
Mutation 必須 不要
TypeScript対応 定義が煩雑な傾向 型安全かつ補完が効きやすい
APIスタイル Options API中心 Composition / Options 両対応

✅ 主なメリット

  • Mutationが不要で定義がシンプル
  • TypeScriptとの高い親和性
  • Store定義の柔軟性(関数ベース)

3. 書き方の違い:Options API vs Composition API

Piniaでは、2種類の定義スタイルがサポートされています。どちらもdefineStore()を用いますが、TypeScriptとの相性を考慮するとComposition APIスタイルが推奨される場面が多くなります。

✔ Options APIによる定義

import { defineStore } from 'pinia'

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

✔ Composition APIによる定義

import { defineStore } from 'pinia'
import { ref, computed } from 'vue'

export const useCounterStore = defineStore('counter', () => {
  const count = ref(0)
  const double = computed(() => count.value * 2)
  const increment = () => {
    count.value++
  }

  return { count, double, increment }
})

Composition APIスタイルでは、返却するプロパティに対して型推論が効くため、より堅牢な型定義が可能です。

4. 実際にPiniaを使用する中で得た学び

Piniaを使ってVue 3 + Composition APIの開発を行う中で、状態管理だけでなくAPI通信やリアクティブ性の扱いにおいても重要な学びがありました。
ここでは特に印象的だった2つの点を紹介します。

4-1. Pinia Coladaの useQuery() とは?

Pinia Coladaは、Piniaでの状態管理と非同期データ取得の設計を統合的に扱えるようにするユーティリティライブラリです。内部的には TanStack Query というデータフェッチライブラリをベースにしていますが、defineQueryOptions() + useQuery() によって、クエリの定義と実行をより型安全かつ簡潔に書けるようになっています。

参考:

使用例:

import { defineQueryOptions, useQuery } from '@pinia/colada'
import { fetchUser } from '@/api'

// クエリの定義:引数でユーザーIDを受け取り、データを取得
const userQuery = defineQueryOptions((id: number) => ({
  key: ['user', id],
  query: () => fetchUser(id),
}))

// useQueryを使ってクエリを実行(引数は直接値)
const { data: user } = useQuery(userQuery, 1)

// 引数を関数として渡すことで動的に実行も可能
const { data: dynamicUser } = useQuery(userQuery, () => props.userId)

このように、Pinia Colada の useQuery() を使うことで、クエリの定義・実行・状態取得の責務を自然に分離しつつ、PiniaのComposition APIストアと統合可能になります。

CompositionAPIにおけるuseQuery()のfetchタイミング

以下のようなコードでは、onMounted()を使用しなくてもAPIリクエストが自動で発行されます。

setup() {
  const { data, isLoading } = useQuery(['user'], fetchUser)
}

なぜonMountedが不要なのか?

  • useQuery()setup() 実行時に初期化されるため、onMounted() を使わなくても初回描画前に自動でAPIリクエストが開始される。なお、この挙動は enabled オプションで制御可能

  • Vueのライフサイクルにおいて、setup()onBeforeMount() よりも前に実行される

つまり、画面の初回描画よりも前にAPIリクエストが開始されるのが正確な挙動です。これは事前フェッチやSSRの最適化にも有効です。

SSR(サーバーサイドレンダリング)とは、サーバー上でHTMLを生成し、それをブラウザに送る仕組み
https://ja.vuejs.org/guide/scaling-up/ssr

4-2. storeToRefs()の活用とリアクティブ性保持

PiniaでComposition APIスタイルを用いてストアを定義した場合、戻り値のオブジェクトはVueのrefcomputedで構成されており、分割代入によってリアクティブ性が失われる可能性があります。

const counter = useCounterStore()
const { count, double } = counter // ⚠ この方法は非推奨

これは、PiniaのstoreオブジェクトがProxyラップされているため、直接分割代入するとVueのリアクティブシステムから外れてしまうためです。

解決方法:storeToRefs()を使用

import { storeToRefs } from 'pinia'

const counter = useCounterStore()
const { count, double } = storeToRefs(counter) // ✅ リアクティブ性が維持される

storeToRefs()は、storeから取得したrefcomputedを適切に分解し、Vueコンポーネント内でもリアクティブに扱えるように変換してくれるヘルパー関数です。

5. まとめ

今まで使用したことのないライブラリを使用するときは、これまで使っていたライブラリの使い方を前提として考えてしまうことのないよう、違いを理解しておく必要があると感じました。
推奨される新しい技術に適応し、知識も、開発するプロダクトも継続的にアップデートしていきたいです。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?