As an old Vue state management library, Vuex is familiar to everyone

Pinia is a new state management library developed specifically for Vue by the vue.js team members and has been incorporated into the official Github

Why develop a Pinia when you have a Vuex?

Let’s take a look at the proposal for vEX5, what should the next generation of Vex5 look like

Pinia completely meets the function points mentioned in his Vuex5 proposal at that time, so it is not too much to say that Pinia is Vuex5, because its author is the official developer and has been taken over by the official. However, at present Vuex and Pinia are still two independent warehouses. It could merge or go it alone, but Pinia is definitely the official recommendation

Because using Vuex in Vue3 requires using Vuex4 and can only be used as a transitional choice, there are great defects, so after the birth of Componsition API, a new state management Pinia was designed

Pinia and Vuex

Vuex: State, Gettes, Mutations, Actions

Pinia: State, Gettes, Actions(both synchronous and asynchronous support)

The latest version of Vuex is 4.x

  • Vuex4 for Vue3
  • Vuex3 for Vue2

The latest version of Pinia is 2.x

  • Both Vue2 and Vue3 are supported

For now, Pinia is much better than Vuex and solves many of Vuex’s problems, so I highly recommend using Pinia directly, especially for TypeScript projects

Pinia core features

  • Pinia noMutations
  • ActionsSupport synchronous and asynchronous
  • There is no nested structure for modules
    • Pinia provides flat structure by design, meaning that each store is independent of each other and no one belongs to each other, which means flat, better code segmentation and no namespaces. You can also implicitly nest stores by importing another module into one module, or even have cyclic store dependencies
  • betterTypeScriptsupport
    • There is no need to create custom complex wrappers to support TypeScript typing of everything, and the API is designed to use TS type inference as much as possible
  • No need to inject, import functions, call them, enjoy automatic completion, making our development more convenient
  • There is no need to manually add a Store, its modules are automatically registered when created by default
  • Both Vue2 and Vue3 are supported
    • Except for initial installation and SSR configuration, both use the same API
  • supportVue DevTools
    • Track the timeline for Actions, Mutations
    • The module itself can be observed in the component that uses the module
    • Support for time-travel makes debugging easier
    • In Vue2 Pinia will use all of Vuex’s interfaces, so they can’t be used together
    • However, debugging tool support for Vue3 is not perfect, such as time-travel
  • Module hot update
    • Modules can be modified without reloading the page
    • Hot updates keep any existing state
  • Support for extending Pinia functionality using plug-ins
  • Support for server-side rendering

Pinia use

Take Vue3 + TypeScript as an example

The installation

npm install pinia
Copy the code

Main. ts Initial configuration

import { createPinia } from 'pinia'
createApp(App).use(createPinia()).mount('#app')
Copy the code

For example, to create a user.ts in the store directory, we define and export a module named user

import { defineStore } from 'pinia'
export const userStore = defineStore('user', {
    state: () = > {
        return { 
            count: 1.arr: []}},getters: {... },actions: {... }})Copy the code

DefineStore takes two arguments

The first parameter is the module name, which must be unique. Multiple modules cannot have the same name. Pinia will mount all modules to the root container

  • Among themstateTo store the global state, it must be an arrow function, in order to avoid cross-request data state contamination during server rendering, so it must be a function, and the arrow function must be used for better TS type derivation
  • gettersIt’s used to encapsulate computational properties, and it has caching capabilities
  • actionsIt is used to encapsulate business logic and modify state

Access to the state

Let’s say we want to access the property count in state in the page

Because defineStore returns a function, you need to call it to get the data object and then use it directly in the template

<template>
    <div>{{ user_store.count }}</div>
</template>
<script lang="ts" setup>
import { userStore } from '.. /store'
const user_store = userStore()
/ / deconstruction
// const { count } = userStore()
</script>
Copy the code

For example, it is perfectly fine to deconstruct the data and use it as in the comments, but note that the data is not reactive. To deconstruct the data and keep it reactive, a method called storeToRefs() is used, as shown in the following example

<template>
    <div>{{ count }}</div>
</template>
<script lang="ts" setup>
import { storeToRefs } from 'pinia'
import { userStore } from '.. /store'
const { count } = storeToRefs(userStore)
</script>
Copy the code

The reason is that Pinia has actually processed the state data reactive, which is the same as Vue3’s Reactive. The deconstructed data is not responsive, so it needs to make the ref responsive proxy again

getters

This, like Vuex’s Getters, also has caching. The following is used several times in the page, the first time getters is called, and the cache is read later if the data has not changed

<template>
    <div>{{ myCount }}</div>
    <div>{{ myCount }}</div>
    <div>{{ myCount }}</div>
</template>
Copy the code

Notice the difference between the two methods, it’s in the comments

getters: {
    // Method one accepts an optional argument, state
    myCount(state){
        console.log('called') // it is used three times in the page, but it is only executed once and then cached
        return state.count + 1
    },
    // Use this instead of passing an argument
    // But you must specify the type of the function return value, otherwise the type cannot be derived
    myCount(): number{
        return this.count + 1}}Copy the code

Update and actions

There are four ways to update data in state. Let’s look at three simple updates, all described in comments

<template>
    <div>{{ user_store.count }}</div>
    <button @click="handleClick">button</button>
</template>
<script lang="ts" setup>
import { userStore } from '.. /store'
const user_store = userStore()
const handleClick = () = > {
    / / method
    user_store.count++
    
    $patch = $patch = $patch = $patch = $patch = $patch = $patch = $patch
    user_store.$patch({
        count: user_store.count1++,
        // arr: user_store.arr.push(1) // Error
        arr: [ ...user_store.arr, 1 ] // Yes, but there is no need to deconstruct the entire array
    })
    
    // Using $patch is better because multiple data updates update the view only once
    
    $patch = $patch; $patch = $patch
    user_store.$patch( state= > {
        state.count++
        state.arr.push(1)})}</script>
Copy the code

The fourth method is to encapsulate actions in the example store/user.ts when there is a lot of logic or requests

You can also use this.xx to retrieve data directly from state. Note that you can’t use arrow functions to define actions

actions: {
    changeState(num: number){ // Arrow functions cannot be used
        this.count += num
    }
}
Copy the code

call

const handleClick = () = > {
    user_store.changeState(1)}Copy the code

Support VueDevtools

Open the Developer tools Vue Devtools to find Pinia, and you can manually modify the data for debugging, which is very convenient

Mock call interface

Example:

Let’s start by defining the sample interface API /user.ts

// Interface data type
export interface userListType{
    id: number
    name: string
    age: number
}
// Simulate the data returned by the request interface
const userList = [
    { id: 1.name: 'Joe'.age: 18 },
    { id: 2.name: 'bill'.age: 19},]// Encapsulates a timer that simulates asynchronous effects
async function wait(delay: number){
    return new Promise((resolve) = > setTimeout(resolve, delay))
}
/ / interface
export const getUserList = async() = > {await wait(100) // Delay the return by 100 ms
    return userList
}
Copy the code

The call interface is then wrapped in actions in Store /user.ts

import { defineStore } from 'pinia'
import { getUserList, userListType } from '.. /api/user'
export const userStore = defineStore('user', {
    state: () = > {
        return {
            // List of users
            list: [] as userListType // Type is converted to userListType}},actions: { 
        async loadUserList(){
            const list = await getUserList()
            this.list = list
        }
    }
})
Copy the code

The page invokes actions to initiate a request

<template>
    <ul>
        <li v-for="item in user_store.list">.</li>
    </ul>
</template>
<script lang="ts" setup>
import { userStore } from '.. /store'
const user_store = userStore()
user_store.loadUserList() // Load all data
</script>
Copy the code

Modify data across modules

In the actions of one module you need to modify the state data of another module

Example: Change the name of a user in the Chat module

// chat.ts
import { defineStore } from 'pinia'
import { userStore } from './user'
export const chatStore = defineStore('chat', {
    actions: { 
        someMethod(userItem){
            userItem.name = 'New name'
            const user_store = userStore()
            user_store.updateUserName(userItem)
        }
    }
})
Copy the code

The user in the module

// user.ts
import { defineStore } from 'pinia'
export const userStore = defineStore('user', {
    state: () = > {
        return {
            list: []}},actions: { 
        updateUserName(userItem){
            const user = this.list.find(item= > item.id === userItem.id)
            if(user){
                user.name = userItem.name
            }
        }
    }
})
Copy the code

conclusion

If this article is of any help to you, please click a “like” to support it. Every “like” from you is the biggest motivation for my creation. Thank you for your support

Scan the code to pay attention to the public number, you can add my friends, I pull you into the front exchange group, we communicate and progress together