Pinia: Next-Generation Vue State Management
Pinia is Vue's official state management library, designed to be type-safe, modular, and very friendly to the Composition API. Compared to Vuex, Pinia's API is more concise, accessing the store in templates is more direct, and it provides excellent TypeScript support.
Why Choose Pinia?
- Simpler API: No mutations, only state, getters, and actions. Actions can be synchronous or asynchronous.
- Complete TypeScript Support: Get perfect type inference without creating complex type declarations.
- Modular Design: Each store is an independent module that can be imported and used on demand.
- Very Lightweight: Very small size, about 1KB.
- Plugin Support: Pinia's functionality can be extended through plugins.
Installation and Configuration
First, install Pinia:
npm install piniaThen, create and use a Pinia instance in your main.js file:
src/main.js
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
const app = createApp(App)
app.use(createPinia()) // Create and use Pinia instance
app.mount('#app')Defining a Store
In Pinia, a store is defined through defineStore(). By convention, we usually create store files in the src/stores/ directory.
src/stores/counter.js
import { defineStore } from 'pinia'
// The first parameter is the store's unique ID
export const useCounterStore = defineStore('counter', {
// state: returns a function to prevent data state pollution caused by cross-requests during server-side rendering
state: () => ({
count: 0,
name: 'Eduardo'
}),
// getters: similar to component's computed properties
getters: {
doubleCount: (state) => state.count * 2,
doubleCountPlusOne() {
// Can access other getters via `this`
return this.doubleCount + 1
}
},
// actions: similar to component's methods, can be asynchronous
actions: {
increment() {
this.count++
},
randomizeCounter() {
this.count = Math.round(100 * Math.random())
}
}
})Using the Store in Components
In the component's <script setup>, you can directly import and call the store's hook function to get the store instance.
CounterComponent.vue
<script setup>
import { useCounterStore } from '@/stores/counter'
// Get the store instance
const counter = useCounterStore()
// Can directly use the store's state, getters, and actions in the template
// counter.count
// counter.doubleCount
// counter.increment()
</script>
<template>
<div>
<p>Count: {{ counter.count }}</p>
<p>Double Count: {{ counter.doubleCount }}</p>
<button @click="counter.increment">Increment</button>
</div>
</template>Destructuring the Store
If you directly destructure from a Pinia store, you'll lose its reactivity. To solve this problem, you can use storeToRefs.
<script setup>
import { storeToRefs } from 'pinia'
import { useCounterStore } from '@/stores/counter'
const counter = useCounterStore()
// `storeToRefs` converts state and getters into reactive refs
const { count, doubleCount } = storeToRefs(counter)
const { increment } = counter // actions can be destructured directly
</script>
<template>
<div>
<p>Count: {{ count }}</p>
<p>Double Count: {{ doubleCount }}</p>
<button @click="increment">Increment</button>
</div>
</template>Pinia is rapidly becoming the preferred solution for state management in Vue 3 projects due to its concise and powerful features.