SoFunction
Updated on 2025-04-05

A detailed introduction to Pinia, a new member of Vue ecosystem

Pinia is a state management solution for Vue applications and is developed by members of the Vuex core team. It feels more like a regular old javascript import module, implementing a lot of Vuex5 proposals.

Pinia supports both Vue2 and Vue3, but the examples shown below all use Vue3, and the version of Pinia is [email protected].

The Pinia versions used by Vue2 and Vue3 are a little different, so please check the official onePinia Documentationfor more information.

Installation and configuration

You can use npm or yarn to install Pinia

yarn add pinia@next
# or use npmnpm install pinia@next

After installation, find the file of the Vue application (the project initialized by vue-cli, generally named). Import createPinia from Pinia's npm package and use Vue's use method as a plugin

//
import { createApp } from 'vue'
import App from './'
import router from './router'
import { createPinia } from 'pinia'

createApp(App)
  .use(router)
  .use(createPinia())
  .mount('#app')

Next, let's create a store

Store Core

Pinia's Store is different from Vuex's Store. When using Vuex, the entire application has only one main store (although you can split it into different modules, it is still a main storage exit). However, today's protagonist Pinia is a modular design out of the box, and does not require a major store, and can create different stores. For example, we can create a logged in user store.

// store/
import { defineStore } from 'pinia'

export const useLoggedInUserStore = defineStore({
// id is required and unique in all Stores.  Because Pinia will display it in devtools  id: 'loggedInUser',
  state () {
    return {
      name: 'Too cold',
      age: 18,
      email: 'fake@'
    }
  },
  getters: {},
  actions: {}
})

Above we created a logged-in user store. Next we can directly reference it in the component, and then call useLoggedInUserStore in the setup function.

<template>
  <div>PiniaApage</div>
  <div>Name:{{}}</div>
  <div>age:{{}}</div>
</template>

<script>
import { useLoggedInUserStore } from '@/store/'
export default {
  name: 'PiniaDemoPage1',
  setup () {
    const user = useLoggedInUserStore()
    return {
      user
    }
  }
}
</script>

This is very different from Vuex. Vuex Store will automatically mount to the current Vue instance and can be called through this.$store. However, the Pania approach also makes it clearer for developers where the Store comes from, as it is a standard Javascript module import that shows which file it was imported from.

If you don't use the Composition API method, you can still use some helper functions to use Pinia. I will talk about it in detail below, let me mention it first.

State

We set the state property in the Store to a function that returns an object containing different state values. This is very similar to how we define data in a component. In fact, the only difference is the attribute name: status and data

import { defineStore } from 'pinia'
export const useLoggedInUserStore = defineStore({
// id is required and unique in all Stores.  Because Pinia will display it in devtools  id: 'loggedInUser',
  state () {
    return {
      name: 'Too cold',
      age: 18,
      email: 'fake@'
    }
  },
  getters: {},
  actions: {}
})

Now, in order to access the state of loginUserStore from the component, we only need to reference the Store we need, which is very elegant. There is no need to find the Store we need from nested objects like Vuex.

<template>
  <div>PiniaApage</div>
  <div>Name:{{}}</div>
  <div>age:{{}}</div>
</template>

<script>
import { useLoggedInUserStore } from '@/store/'
export default {
  name: 'PiniaDemoPage1',
  setup () {
    //No need to think about the previous method    const user = useLoggedInUserStore()
    return {
      user
    }
  }
}
</script>

Warning: User cannot be structured, because it will lose its responsiveness. The following method is wrong.

❌ const {name, email} = useLoggedInUserStore()

If you are not using the Composition API method, but the Option API method. State data can be obtained through Pinia's mapState function. Although Pinia's mapState function and Vuex's mapState have the same name, they are used completely differently.

The first parameter of Pinia's mapState function must be the Store created previously, and the second parameter is the property value of the state in the Store. Look at the code

//
<template>
  <h1>Hello, I am {{name}},I'm from Earth</h1>
  <h2>Contact Email:{{email}}</h2>
</template>
<script>
import {mapState} from 'pinia'
export default{
  computed:{
    ...mapState(useLoggedInUserStore, ['name','email'])
  }
}
</script>

Summarize:

  • The way to define Pinia's state is the same as the component's data
  • We need to manually import the Store modules we need between components. The advantage of this is to know the source of the data clearly and comply with the standard Javascript

Getters

Getters in Pinia and getters in Vuex have the same functions, both of which are computed as component properties.
There are two ways to create getters, one is to use this keyword, and the other is to view the code specifically through state.

//store/
import { defineStore } from 'pinia'

export const usePostsStore = defineStore({
  id: 'PostsStore',
  state: ()=>({ posts: ['post 1', 'post 2', 'post 3', 'post 4'] }),
  getters:{

    // Traditional function method    postsCount: function(){
      return 
    },
    // Traditional function abbreviation    postsCount2(){
      return 
    },
    // Arrow function    postsCount3:(state)=>,

    // ❌ The arrow function + this method cannot be used, this method is incorrect    // postsCount: ()=> 
  }
})

Next, let’s look at how to use the created getters in the Component API method. The usage is the same as state. Look at the code

<template>
  <div>PiniaBpage</div>
  <div> total:{{}}</div>
</template>

<script>
import { usePostsStore } from '@/store/'
export default {
  name: 'PiniaBpage',
  setup () {
    const postsStore = usePostsStore()
    return {
      postsStore
    }
  }
}
</script>

If it is a component of the Option API, it cannot be obtained through the mapGetters helper function like Vuex. Because Pinia does not have mapGetters helper function, Pinia consumes getters with mapState helper function

<template>
  <div> total:{{postsCount}}</div>
</template>

<script>
import { mapState } from 'pinia'
import { usePostsStore } from "@/store/PostsStore";

export default {
  computed:{
    ...mapState(usePostsStore, ['postsCount'])
  }
};
</script>

Actions

Pinia is different from Vuex. Pinia provides a single way to change the value of state. In Pinia, there are no mutations, only action methods. Let’s first take a look at how to use Pinia’s action. On the code

  • Directly find the corresponding state modification through this
  • Pass the .$patch function method
  • Pass .$patch object method
import { defineStore } from 'pinia'

export const usePostsStore = defineStore({
  id: 'PostsStore',
  state: () => ({
    posts: ['post 1', 'post 2', 'post 3', 'post 4'],
    user: { postsCount: 2 },
    age:18,
    errors: []
  }),
  getters: {
    postsCount: function () {
      return 
    },
    postsCount2 () {
      return 
    },
    // Arrow function    postsCount3: (state) => 
  },
  actions: {
    insertPost () {
      //Method 1: Directly find the corresponding state modification through this      (`post_${()}`)
      ++
    },
    removePost () {
      //Method 2: Pass the .$patch function method      this.$patch((state) => {
        ()
        ++
      })
      
      //By .$patch object method      this.$patch({
        age:30
      });
    }
  }
})

The above shows three ways to change Pinia's State.

If it is the Composition API usage

<template>
  <div>PiniaBpage</div>
  <div> total:{{}}</div>
  <ul>
    <li
      v-for="item in "
      :key="item"
    >
      {{item}}
    </li>
  </ul>
  <button @click="handleAdd">New</button>
  <button @click="handleRemove">delete</button>
  <button @click="handleBeforeAdd">New到前面</button>
</template>

<script>
import { usePostsStore } from '@/store/'
  
export default {
  name: 'PiniaBpage',
  setup () {
    const postsStore = usePostsStore()

    // Added    const handleAdd = () => {
      ()
    }

    // delete    const handleRemove = () => {
      ()
    }
    // Added to the previous one, you can also modify it through $patch here, and you can also modify it directly here   const  handleBeforeAdd=()=>{
     postsStore.$patch((state) => {
        ()
        ++
     })
   }
    return {
      postsStore,
      handleAdd,
      handleRemove,
      handleBeforeAdd
    }
  }
}
</script>

If it is the use of the Options API, you need to use the helper function mapActions

// 
<template>
  <input type="text" v-model="post" />
  <button @click="insertPost(post)">save</button>
</template>
<script>
import {mapActions} from 'pinia'
import { usePostsStore } from '@/store/PostsStore';
export default{
  data(){
    return { post: '' }
  }, 
  methods:{
    ...mapActions(usePostsStore, ['insertPost'])
  }
}
</script>

In fact, Pinia's action is very flexible

  • Can be called in components or other actions
  • Can be called in other Store actions
import { useAuthStore } from './auth-store'

export const useSettingsStore = defineStore('settings', {
  state: () => ({
    // ...
  }),
  actions: {
    async fetchUserPreferences(preferences) {
      const auth = useAuthStore()
      if () {
         = await fetchPreferences()
      } else {
        throw new Error('User must be authenticated')
      }
    },
  },
})
  • Supports synchronization and asynchronous
  • Can support flexible parameters
  • ...............

Vue Devtools

In Vue 2, Pania supports viewing status in the Vuex tab and even seeing time tracks. The labels for the time track are hardly as good as they are when used in Vuex.
As for Vue 3, Pania only supports viewing status in devtools and does not support the time track function. However, this is actually more than Vuex provides for Vue 3, as it is not supported at all in the latest development tools.

at last

A quick review of the most significant features of Pinia to help you quickly understand Pinia and apply it to your projects

  • Maintained by core team members
  • It feels more like a regular old javascript import module, operating as a method call, accessing state directly on the store, etc.
  • No more mutations
  • Integrate with Vue Devtools

in conclusion

Although Pinia is a new member of the Vue ecosystem, it turns out that Pinia is the optimal and promising state management solution, with an intuitive API, modularity, and clear import source.

References

Pinia, an Alternative Store
Official website

This is the end of this article about the detailed introduction of Pinia, a new member of the Vue ecosystem. For more related Vue Pinia content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!