A new generation of state management tools -- Pinia Getting Started Guide

1: Pinia introduction and five advantages

Pinia is a replacement for Vuex in the vue ecosystem, a brand new vue state management library. After Vue3 became the official version, the project strongly recommended by You Yuxi is Pinia.
Let's first take a look at what Pinia is better than Vuex, that is, the five major advantages of Pinia.

  1. It can support Vue2 and Vue3 very well, that is, old projects can also use Pinia.
  2. Abandon the operation of Mutations, only state, getters and actions. It greatly simplifies the use of state management libraries, making code writing easier and more intuitive.
  3. There is no need for nested modules, and it complies with Vue3's Composition api to make the code more flat.
  4. Full TypeScript support. A major advantage of the Vue3 version is the support for TypeScript, so Pinia has also achieved complete support. If you are familiar with Vuex, you must know that Vuex's syntax support for TS is not complete (it is often complained about).
  5. The code is more concise and can achieve good automatic code segmentation. In the era of Vue2, writing code requires scrolling back and forth on the screen to find variables, which is very troublesome. The Composition API of Vue3 perfectly solves this problem. Automatic code segmentation can be realized, and pinia also inherits this advantage.

If you say that these five points are a bit too much, I can't remember them. It can be briefly summarized that the advantages of Pinia are more concise syntax, perfect support for Vue3's Composition api and perfect support for TypesCcript. These advantages and You Yuxi's strong recommendation. I personally think that Pinia will completely replace Vuex soon and become the most suitable state management library for Vue3.

Two: Pinia development environment installation

Here I use Vite to create a Vue3 project as an example.

1. To use Vite, you need to initialize vite first:

npm init vite@latest

2. Start the project:
insert image description here

npm install
npm run dev

3.pinia installation:

npm install pinia

insert image description here

You can see that the latest version of pinia installed is 2.0.12

Three: Create a store in the way of Pinia

1. Introduce Pinia in the main.ts file
import {
    
     createPinia } from 'pinia'

After the introduction, use the createPinia() method to get an instance of pinia, and then mount Pinia to the Vue root instance.
insert image description here

2. Create a store state management library

Create a new store folder directly under the /src directory. Once you have the folder, create an index.ts file.

In the code in this file, we generally only do three things:

  1. Define state container (repository)
  2. Modify the state in the container (warehouse)
  3. The use of actions in the warehouse

Step 1: Define the state container (warehouse)

import {
    
     defineStore} from 'pinia'

export const mainStore = defineStore('main',{
    
    
  state:()=>{
    
    
    return {
    
    }
  },
  getters:{
    
    },
  actions:{
    
    }
})

After writing this code, you will feel that this is a Vue widget, which is also an advantage of Pinia.

  • The first parameter of the defineStore( ) method: it is equivalent to giving the container a name. Note: The name here must be unique and cannot be repeated.
  • The second parameter of the defineStore( ) method: it can be simply understood as a configuration object, which contains configuration instructions for the container warehouse. Of course this specification is in the form of an object.
  • state attribute: used to store the global state, defined here can be the global state in the SPA.
  • Getters attribute: used to monitor or calculate state changes, and has a cache function.
  • Actions attribute: For the business logic of data changes in the state, the requirements are different, and the writing logic is different. To put it bluntly, it is to modify the state global state data.

Step 2: We define a State in the Store, and here we write Hello Pinia!.

state:()=>{
    
    
   return {
    
    
     helloPinia:'Hello Pinia!'
   }
},

At this time, helloPinia is the global state data, which can be read by each page and component through the Pinia method.

3. Read Store data in vue3 component

In \src\components, create a Hyy.vue component. code show as below:

First introduce mainStore, and then get the store instance through mainStore, then you can call the state data defined by the state in the store in the component

<template>
  <div>
      <h2 class="">{
    
    {
    
     store.helloPinia}}</h2>
  </div>
</template>

<script lang="ts">
import {
    
     mainStore } from "../store/index";
export default{
    
    
  setup(){
    
    
      const store = mainStore();
      
      return{
    
    
          store,
      }
  }
}
</script>

After writing this component, import it into App.vue and use it:

<script setup lang="ts">
  import Hyy from "./components/Hyy.vue";
</script>

<template>
  <Hyy />
</template>

<style>
</style>

Four: Pinia changes status data and precautions

1. Create a new component to realize the change of state data

To demonstrate the concept of a data warehouse, create a new component. Then modify the state data in one component to see if the data in another component changes.

Create a new file CountButton.vue under the \components\ folder.

<template>
    <h2 class="">{
    
    {
    
     store.helloPinia }}</h2>
</template>

<script lang="ts">
import {
    
     mainStore } from "../store/index";
export default{
    
    
    setup(){
    
    
        const store = mainStore();

        return{
    
    
            store,
        }
    }
}
</script>

Because what is going to be done here is a component that can be counted, so first add a state data count: 0 to the state attribute of \store\index.ts.

\src\store\index.ts file

state:()=>{
    
    
  return {
    
    
    helloWorld:'HelloWorld',
    count:0
  }
},

After having this state data, go back to the \components\CountButton.vue file and add buttons and corresponding business logic (note that the business logic here is to modify the state data). code show as below:

<template>
    <div>
        <button @click="handleClick">点击增加</button>
    </div>
</template>

<script lang="ts">
import {
    
     mainStore } from "../store/index";
export default{
    
    
    setup(){
    
    
        const store = mainStore();
        const handleClick = () => {
    
    
            store.count ++
        }

        return{
    
    
            store,
            handleClick
        }
    }
}
</script>

After writing, we display the count in the Hyy.vue component.

\src\components\Hyy.vue

<template>
    <div>
        <h2>{
    
    {
    
     store.helloPinia }}</h2>
        <h2>{
    
    {
    
     store.count }}</h2>
    </div>
</template>

<script lang="ts">
import {
    
     mainStore } from "../store/index";
export default{
    
    
    setup(){
    
    
        const store = mainStore();

        return{
    
    
            store,
        }
    }
}
</script>

Then add CountButton to the App.vue page.

<script setup lang="ts">
  import Hyy from "./components/Hyy.vue";
  import CountButton from "./components/CountButton.vue";
</script>

<template>
  <Hyy />
  <CountButton />
</template>

<style>
</style>

After completing this step, you can go to the browser to check the final implementation effect. If everything is normal, you can see that after we click the button, the data of the two components can be linked through the state management of Pinia.

Note: Don't step on the pit of the structure

I found such a pit when I was studying, and I will share it with you here. I hope you guys don't step on the pit. Looking at the code below, can we simplify it a bit

<template>
    <div>
        <h2>{
    
    {
    
     store.helloPinia }}</h2>
        <h2>{
    
    {
    
     store.count }}</h2>
    </div>
</template>

<script lang="ts">
import {
    
     mainStore } from "../store/index";
export default{
    
    
    setup(){
    
    
        const store = mainStore();

        return{
    
    
            store,
        }
    }
}
</script>

We can deconstruct the store, and then read the data directly from the template.

<template>
    <div>
        <h2>{
    
    {
    
     store.helloPinia }}</h2>
        <h2>{
    
    {
    
     store.count }}</h2>
        
        <hr/>

        <h2>{
    
    {
    
     helloPinia }}</h2>
        <h2>{
    
    {
    
     count }}</h2>
    </div>
</template>

<script lang="ts">
import {
    
     mainStore } from "../store/index";
export default{
    
    
    setup(){
    
    
        const store = mainStore();
        const {
    
    helloPinia,count} = store;
        return{
    
    
            store,
        }
    }
}
</script>

This seems simple, but the deconstructed data has only one effect, not responsive data (this is the pit I stepped on). That is to say, when you change the data state, the deconstructed state data will not change. If we click the Add button again at this time, we can see that only the unstructured data has changed.

So I started to look for official documents, and apparently the Pinia team also discovered this problem and provided the storeToRefs( ) method. This method is in Pinia, so we use import first.

import {
    
     storeToRefs } from "pinia";

With the storeToRefs( ) method, you can use the method on the store in the deconstructed code. In fact, at this time, the deconstructed data is used as a ref responsive proxy. So the data has responsive capabilities.

const {
    
     helloWorld, count } = storeToRefs(store);

At this time, go to the browser to test again, and everything is normal. Supplement: In fact, in Vuex, it is not possible to directly deconstruct data.

Five: Multiple ways of Pinia to modify status data

The modification of status data has been preliminarily explained above, which is very simple. But this is only one way of data modification, there are three ways.

The second method: use $patch to modify multiple pieces of data

Next to the CountButton.vue component written above, we write another method handleClickPatch( ). We use $patch in Pinia to write.

\scr\components\CountButtton.vue

const handleClickPatch = () => {
    
    
    store.$patch({
    
    
        count:store.count + 2 
    })
}

Then add a button in it, and execute this method after clicking.

<button @click="handleClickPatch">点击增加 - patch</button>

Of course, when I am modifying a single piece of data, I like this direct modification method store.count++, because it is simple enough. But if you modify multiple pieces of data at the same time, it is recommended that you use the $patch method.

For example, when we click the button now, we modify the state data helloPinia at the same time, which can be written in this way:

const handleClickPatch = () => {
    
    
    store.$patch({
    
    
        count:store.count + 2,
        helloPinia:store.helloPinia === 'Hello Pinia!' ? 'Hello World!' : 'Hello Pinia!' 
    })
}

Then you said that if I write two lines of code directly in handleClick, can this effect also be achieved. It is achievable through code testing. Which why use $patch to do it?

const handleClick = () => {
    
    
    store.count ++
    store.helloPinia = store.helloPinia === 'Hello Pinia!' ? 'Hello World!' : 'Hello Pinia!'
}

Because the official website of Pinia has clearly stated that the method of $patch is optimized, which will speed up the modification speed and greatly benefit the performance of the program. So if you are updating status data for multiple pieces of data at the same time, it is recommended to use the $patch method to update.

Full code:

<template>
    <div>
        <button @click="handleClick">点击增加</button>
        <button @click="handleClickPatch">点击增加 - patch</button>
    </div>
</template>

<script lang="ts">
import {
    
     mainStore } from "../store/index";
export default{
    
    
    setup(){
    
    
        const store = mainStore();
        const handleClick = () => {
    
    
            store.count ++
            store.helloPinia = store.helloPinia === 'Hello Pinia!' ? 'Hello World!' : 'Hello Pinia!'
        }
        const handleClickPatch = () => {
    
    
            store.$patch({
    
    
                count:store.count + 2,
                helloPinia:store.helloPinia === 'Hello Pinia!' ? 'Hello World!' : 'Hello Pinia!' 
            })
        }

        return{
    
    
            store,
            handleClick,
            handleClickPatch
        }
    }
}
</script>
The third method: modify the state data in the form of $patch plus function

In the $patch method above, our parameter uses an object. Another way is to transfer functions. This method is suitable for the modification of complex data, such as the modification of arrays and objects.

Write another method handleClickMethod( ), and pass an arrow function into it.

const handleClickMethod = ()=> {
    
    
    store.$patch((state)=>{
    
    
        state.count ++
        state.helloPinia = state.helloPinia === 'Hello Pinia!' ? 'Hello World!' : 'Hello Pinia!' 
    })
}

The state at this time is the state in the store warehouse, so we can directly change the value of any state data in the function. In order to see the effect, let's write a button to execute this method.

<button @click="handleClickPatch">点击增加 - $patch+函数</button>
The fourth type: write the logic in actions, and then call actions

If you have a very complicated modification process, you can define the functions in actions in the store first, and then call the functions in the components.

Let's first go to the \src\store\index.ts file and write a changeState( ) method in the place of actions to change the data state. code show as below:

actions: {
    
    
    changeState(){
    
    
        this.count++
        this.helloPinia = 'change helloPinia!!!'
    }
}

With this changeState( ) function, you can call this function in the component to modify the state data. Go to the \src\components\CountButton.vue file. Write a new method handleClickActions( ) method. Then you can use the store to call the changeState( ) method.

const handleClickActions = ()=>{
    
    
    store.changeState()
}

Then add a button and call this method.

<button @click="handleClickActions">点击增加 - actions</button>

Note: When using actions, arrow functions cannot be used, because the arrow function binding is the external this. You guys need to pay attention to this one.

Six: Use of Getters in Pinia

1. Add state properties and write Getters

First add a phone state data in the state of the \src\store\index.ts file.

state:() => {
    
     
    return {
    
    
        helloPinia: 'Hello Pinia!',
        count: 0,
        phone:'17808098401'
    } 
},

Then write a method in the getters, this method is to hide the middle four digits of the mobile phone number, the hidden method is to use regular expressions to replace. code show as below:

getters:{
    
    
  phoneHidden(state){
    
    
    return state.phone.toString().replace(/^(\d{3})\d{4}(\d{4})$/, '$1****$2')
  }
},

Then go to \src\components\Hyy.vue to directly display the hidden number display:
insert image description here
Open the browser at this time, and you can see that the phone number has been hidden.

2. Cache feature of Getters

Getters have caching features. Now we call phoneHidden twice in our Hyy component. At this time, we add a console.log('PhoneHidden was called') in the index.ts state warehouse.

 getters: {
    
    
     phoneHidden(): string{
    
    
         console.log('PhoneHidden被调用了')
         return this.phone.toString().replace(/^(\d{3})\d{4}(\d{4})$/, '$1****$2')
     }
 },

Then go back to the browser and press F12 to open and view the Console panel. You can see that PhoneHidden is called only once, which also shows that the getters are cached. Although they are called multiple times, the same value will not be called multiple times. .

Under the \src\components\CountButton.vue file, write a new method handleClickChangePhone. Used to change the phone number.

// 点击按钮的对应函数
const handleClickChangePhone = () => {
    
    
    store.phone = "17800000000";
}

After having the function, write a button, trigger this function, and the phone number will change.

<button @click="handleClickChangePhone">Getter缓存</button>

When the phone number changes, the Getters will work automatically, and the corresponding phoneHidden method will also be called once to clear the previous data cache.

3. About the use of this

After writing the small case above, I believe you have mastered the use of Pinia's Getters. At this time, go back to the \src\store\index.ts file. We see that the this keyword can be directly used in actions.

Then let's think about a question, can this be used in getters to operate?

The answer is yes, modify the code to the following form:

getters: {
    
    
    phoneHidden():string{
    
    
        return this.phone.toString().replace(/^(\d{3})\d{4}(\d{4})$/, '$1****$2')
    }
},

Because we are using TS, if we do not pass the state, TypeScript cannot automatically push out the returned data type, so here we need to mark the return type as String. It will not prompt an error.

Summary: In this section we learned the usage of Getters in Pinia, which is very similar to computed properties in Vue, but has cached properties. When we write Getters, we can not only pass the state parameter to change the state data, but also directly use the keyword this to change the data.

Seven: Mutual call of Store in Pinia

In the above code, we have only used one Store warehouse. In real projects, we often have multiple Stores. When there are multiple Stroes, there will be a problem of calling each other inside the Store.

Step 1: Create a new Store warehouse

Create a new Hyy.ts file under \src\store:

import {
    
     defineStore } from 'pinia'

export const hyyStore = defineStore("hyyStore", {
    
    
    state:() => {
    
     
        return {
    
    
            list:["黄黄","小黄","黄小黄"]
        } 
    },
    getters: {
    
    
    },
    actions: {
    
    
    }
})

This is a very simple warehouse with only state (state data). It should be noted that the ID must be unique. After you have this warehouse, you can go back to the index.ts warehouse and call it.

Step 2: First introduce the Hyy store.
import {
    
     hyyStore } from './hyy'
Step 3: Then add a getList( ) method in the actions section.

This part is very simple to write, just use console.log( ) to print to the console.

actions: {
    
    
    changeState(){
    
    
        this.count++
        this.helloPinia = 'change helloPinia!!!'
    },
    getList(){
    
    
        console.log(hyyStore().list)
    }
}

For the convenience of learning, here is the entire code of \src\store\index.ts:

import {
    
     defineStore } from 'pinia'
import {
    
     hyyStore } from './hyy'

export const mainStore = defineStore("mian", {
    
    
    state:() => {
    
     
        return {
    
    
            helloPinia: 'Hello Pinia!',
            count: 0,
            phone:'17808098401'
        } 
    },
    getters: {
    
    
        phoneHidden():string{
    
    
            return this.phone.toString().replace(/^(\d{3})\d{4}(\d{4})$/, '$1****$2')
        }
    },
    actions: {
    
    
        changeState(){
    
    
            this.count++
            this.helloPinia = 'change helloPinia!!!'
        },
        getList(){
    
    
            console.log(hyyStore().list)
        }
    }
})

In this way, the two stores call each other.

Step 4: In order to see the effect, we still come to the \src\components\CountButton.vue file and write a new method called getList( ).
const getList = () => {
    
    
  store.getList();
};

After having the getList( ) method, in the template part, write a button to trigger.

<button @click="getList">Pinia中store的相互调用</button>

Go to the browser to check the effect, press F12 to open the console, and click the button, you can see that the cross-Store status data call has been successful.

Guess you like

Origin blog.csdn.net/weixin_42365757/article/details/123848276