跳至主要內容

Introduction

Zhao Bin笔记frontendpiniapinia

Introduction

Pinia (pronounced /piːnjʌ/, like "peenya" in English) is a store library for Vue, it allows you to share a state across components/pages.

  • Works for both Vue 2 and Vue 3
  • Optional composition API
  • The same API for SSR.
  • TypeScript support
  • Hot module replacement
  • Plugins

Basic example

Create a store:

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

export const useCounterStore = defineStore('counter', {
  state: () => {
    return {
      count: 0,
    }
  },
  // could also be defined as
  // state: () => ({ count: 0 })
  actions: {
    increment() {
      this.count++
    },
  },
})

And then you use it in a component:

import { useCounterStore } from '@/stores/counter'

export default {
  setup() {
    const counter = useCounterStore()
    counter.count++

    // with autocompletion
    counter.$patch({ count: counter.count + 1 })
    // or using a action instead
    counter.increment()
  },
}
Basic example

You can even use a function (similar to a component setup()) to define a Store for more advanced use cases:

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

  return {
    count,
    increment,
  }
})
setup example

If you are still not into setup() and Composition API,
don't worry, Pinia also support a similar set of map helpers like Vuex.
You define stores the same way but then use mapStores(), mapState(), or mapActions():

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

const useUserStore = defineStore('user', {
  // ...
})

export default {
  computed: {
    // other computed properties
    // ...
    // gives access to this.counterStore and this.userStore
    ...mapStores(useCounterStore, useUserStore),
    // gives read access to this.count and this.double
    ...mapState(useCounterStore, ['count', 'double']),
  },
  methods: {
    // gives access to this.increment()
    ...mapActions(useCounterStore, ['increment']),
  },
}
Option API example

A more realistic example

Here is a more complete example of the API you will be using with Pinia with types even in JavaScript.

import { defineStore } from 'pinia'

export const todos = defineStore('todos', {
  state: () => ({
    /** @type {{ text: string, id: number, isFinished: boolean }[]} */
    todos: [],
    /** @type {'all' | 'finished' | 'unfinished'} */
    filter: 'all',
    // type will be automatically inferred to number
    nextId: 0,
  }),
  getters: {
    finishedTodos(state) {
      // autocompletion
      return state.todos.filter(todo => todo.isFinished)
    },
    unfinishedTodos(state) {
      return state.todos.filter(todo => !todo.isFinished)
    },
    /**
     * @returns  {{ text: string, id: number, isFinished: boolean }[]}
     */
    filteredTodos(state) {
      if (this.filter === 'finished') {
        // call other getters with autocompletion
        return this.finishedTodos
      } else if (this.filter === 'unfinished') {
        return this.unfinishedTodos
      }
      return this.todos
    },
  },
  actions: {
    // any amount of arguments, return a promise or not
    addTodo(text) {
      // you can directly mutate the state
      this.todos.push({
        text,
        id: this.nextId++,
        isFinished: false,
      })
    },
  },
})

Comparison with Vuex

Compared to Vuex, Pinia provides a simpler API with less ceremony, offers Composition-API-style APIs,
and most importantly, has solid type inference support when used with TypeScript.

Comparison with Vuex 3.x/4.x

Vuex 3.x is Vuex for Vue 2 while Vuex 4.x is for Vue 3

Pinia API is very different from Vuex ≤4, namely:

  • Mutations no longer exist
  • TypeScript support
  • No more magic strings to inject
  • No need to dynamically add stores, they are all dynamic by default
  • No more nested structuring of modules, Pinia offers a flat structuring by design
  • No namespaced modules. Given the flat architecture of stores, "namespacing" stores is inherent to how they are defined and you could say all stores are namespaced.

For more detailed instructions on how to convert an existing Vuex ≤4 project to use Pinia, see the Migration from Vuex Guideopen in new window.