One article to understand pinia Vue state management

1 Overview:

pinia is similar to vuex in vue2, which realizes cross-page shared state management, similar to session management in java. But pinia is easier to use than vuex, and has the following advantages:

  1. Execute Vue2 and vue3 at the same time.
  2. There are only state, getter, and action in pinia, and Mutation in Vuex is abandoned
  3. Actions in pinia support synchronous and asynchronous, but Vuex does not.
  4. Good typescript support. After all, we recommend using TS for Vue3 to write. At this time, it is very appropriate to use pinia
  5. There is no need to create nested modules anymore. If there is too much data in Vuex, we usually manage it in modules, which is a bit troublesome. However, each store in pinia is independent and does not affect each other.
  6. The volume is very small, only about 1KB
  7. pinia supports plug-ins to extend its own functions
  8. Support server-side rendering

2. Preparations

If you want to learn pinia, it is best to have a foundation of Vue3 and understand what a combined API is. If you don't know Vue3 yet, it is recommended to learn Vue3 first.

When explaining pinia in this article, it is all based on Vue3. As for how to use pinia in Vue2, you can go to the official pinia website to learn by yourself. After all, there are still a few people using pinia in Vue2.

Project construction:

We are here to build a latest Vue3 + TS + Vite project.

Excuting an order:

npm create vite@latest my-vite-app --template vue-ts

Run the project:

npm install
npm run dev

Delete other useless codes in app.vue, the final page is as follows:
insert image description here

3.pinia installation and use

3.1. Install Pinia

yarn add pinia
# 或者使用 npm
npm install pinia

After the installation is complete, we need to mount pinia to the Vue application, that is, we need to create a root storage and pass it to the application. Simply put, it is to create a data bucket for storing data and put it in the application.

Modify main.js, import the createPinia method provided by pinia, and create root storage.

// main.ts


import {
    
     createApp } from "vue";
import App from "./App.vue";
import {
    
     createPinia } from "pinia";
const pinia = createPinia();


const app = createApp(App);
app.use(pinia);
app.mount("#app");

3.2 The creation and use of store

Creating a store
store simply means a data warehouse, and all our data is placed in the store. Of course, you can also understand it as a public component, but the public component only stores data, which can be accessed and modified by all our other components.

We need to use the defineStore() method provided by pinia to create a store, which is used to store the data we need to use globally.

First, create a new store folder under the project src directory to store the various stores we created, and then create a new user.ts file under this directory, which is mainly used to store user-related stores.

code show as below:

/src/store/user.ts


import {
    
     defineStore } from 'pinia'


// 第一个参数是应用程序中 store 的唯一 id
export const useUsersStore = defineStore('users', {
    
    
  // 其它配置项
})

Creating a store is very simple, just call the defineStore function in pinia, which receives two parameters:

name: a string, mandatory item, the unique id of the store.
options: an object, the configuration items of the store, such as configuring the data in the store, the method of modifying the data, and so on.
We can define any number of stores, because we actually have a store as a function, which is one of the benefits of pinia, which makes our code flat, which is the same as the implementation idea of ​​Vue3.

Before using the store
, we created a store. To put it bluntly, we created a method, so our purpose must be to use it. If we want to use it in App.vue, how to use it?

/src/App.vue
<script setup lang="ts">
import {
    
     useUsersStore } from "../src/store/user";
const store = useUsersStore();
console.log(store);
</script>

Using the store is very simple, just import the useUsersStore method we declared, we can first look at what is output by executing this method:
insert image description here

Add state
We all know that store is used to store public data, so where does the data actually exist? Earlier we used the defineStore function to create a store. The second parameter of this function is an options configuration item. The data we need to store is placed in the state attribute of the options object.

Suppose we add some task basic data to the store and modify the user.ts code.
code show as below:

export const useUsersStore = defineStore("users", {
    
    
  state: () => {
    
    
    return {
    
    
      name: "小猪课堂",
      age: 25,
      sex: "男",
    };
  },
});

In the above code, we added the state attribute to the configuration item, which is used to store data, and we added 3 pieces of data to the state. It should be noted that state receives the value returned by an arrow function, it cannot directly receive an object.

Manipulating the state
The purpose of storing data in the store is to operate it, so we will try to manipulate the data in the state next.

  • Reading state data
    Reading state data is very simple. We tried to print the store in App.vue before, so let's add data and then look at the print results:
    insert image description here

At this time, we found that there were a few more attributes in the printed result, which happened to be the data we added, and we modified App.vue to display these data.

code show as below:

<template>
  <img alt="Vue logo" src="./assets/logo.png" />
  <p>姓名:{
    
    {
    
     name }}</p>
  <p>年龄:{
    
    {
    
     age }}</p>
  <p>性别:{
    
    {
    
     sex }}</p>
</template>
<script setup lang="ts">
import {
    
     ref } from "vue";
import {
    
     useUsersStore } from "../src/store/user";
const store = useUsersStore();
const name = ref<string>(store.name);
const age = ref<number>(store.age);
const sex = ref<string>(store.sex);
</script>

In the above code, we directly obtained the value stored in the store through store.age, etc., but have you noticed that this is cumbersome, and we can actually use deconstruction to obtain the value to make the code more concise.
The deconstruction code is as follows:

import {
    
     useUsersStore } from "../src/store/user";
const store = useUsersStore();
const {
    
     name, age, sex } = store;
  • Multiple components use state
    The most important purpose of using store is to share data between components, then we create a new child.vue component, and use state data inside the component.
    The child.vue code is as follows:
<template>
  <h1>我是child组件</h1>
  <p>姓名:{
    
    {
    
     name }}</p>
  <p>年龄:{
    
    {
    
     age }}</p>
  <p>性别:{
    
    {
    
     sex }}</p>
</template>
<script setup lang="ts">
import {
    
     useUsersStore } from "../src/store/user";
const store = useUsersStore();
const {
    
     name, age, sex } = store;
</script>

The child component is almost the same as the app.vue component, that is, it simply uses the data in the store.

  • Modify state data

If we want to modify the data in the store, we can directly reassign the value. We add a button in App.vue and click the button to modify a certain data in the store.

code show as below:

<template>
  <img alt="Vue logo" src="./assets/logo.png" />
  <p>姓名:{
    
    {
    
     name }}</p>
  <p>年龄:{
    
    {
    
     age }}</p>
  <p>性别:{
    
    {
    
     sex }}</p>
  <button @click="changeName">更改姓名</button>
</template>
<script setup lang="ts">
import child from './child.vue';
import {
    
     useUsersStore } from "../src/store/user";
const store = useUsersStore();
const {
    
     name, age, sex } = store;
const changeName = () => {
    
    
  store.name = "张三";
  console.log(store);
};
</script>

The above code adds a changeName method to change the value of the name in the store. We click the button to see the final effect: we
insert image description here
can see that the name in the store has indeed been modified, but there seems to be no change on the page, which shows that our use The name is not responsive.

Many friends may say that you can use the monitoring function to monitor store changes and refresh the page...

In fact, pinia provides us with a method to make the attributes such as the name we obtain responsive, and we re-modify the code.

The app.vue and child.vue codes are modified as follows:

import {
    
     storeToRefs } from 'pinia';
const store = useUsersStore();
const {
    
     name, age, sex } = storeToRefs(store);

The method of obtaining state data in our two components is changed to the form of the above code, and the data in sstate is changed to responsive by using the storeToRefs function of pinia.

In addition, we also add a method to change state data to child.vue.

The child.vue code is as follows:

<template>
  <h1>我是child组件</h1>
  <p>姓名:{
    
    {
    
     name }}</p>
  <p>年龄:{
    
    {
    
     age }}</p>
  <p>性别:{
    
    {
    
     sex }}</p>
  <button @click="changeName">更改姓名</button>
</template>
<script setup lang="ts">
import {
    
     useUsersStore } from "../src/store/user";
import {
    
     storeToRefs } from 'pinia';
const store = useUsersStore();
const {
    
     name, age, sex } = storeToRefs(store);
const changeName = () => {
    
    
  store.name = "小猪课堂";
};
</script>
  • reset state

Sometimes we modify the state data and want to restore it, what should we do at this time? For example, the user fills out part of the form and suddenly wants to reset to the original state.

At this point, we can directly call the $reset() method of the store. Continue to use our example and add a reset button.

code show as below:

<button @click="reset">重置store</button>
// 重置store
const reset = () => {
    
    
  store.$reset();
};

When we click the reset button, the data in the store will change to the initial state and the page will be updated.

  • Change the state data in batches
    Before we modify the state data one by one, such as store.name="Zhang San" and so on, if we need to modify many pieces of data at one time, there is an easier way to use the store The $patch method modifies the app.vue code and adds a method to change data in batches.

code show as below:

<button @click="patchStore">批量修改数据</button>
// 批量修改数据
const patchStore = () => {
    
    
  store.$patch({
    
    
    name: "张三",
    age: 100,
    sex: "女",
  });
};

Experienced friends may have discovered that it seems a bit expensive to use this method of batch changes. If some fields in our state do not need to be changed, but according to the above code, we must enumerate all the fields in the state Out.

In order to solve this problem, the $patch method provided by pinia can also receive a callback function, which is a bit like our array loop callback function.

The sample code is as follows:

store.$patch((state) => {
    
    
  state.items.push({
    
     name: 'shoes', quantity: 1 })
  state.hasChanged = true
})

In the above code, we changed the state data in batches, but did not list all the state fields.

  • directly replace the entire state

pinia provides a way for us to directly replace the entire state object, using the $state method of the store.

Sample code:

store.$state = {
    
     counter: 666, name: '张三' }

The above code will replace the state we declared in advance with a new object. Maybe this kind of scene is rarely used, so I won’t explain it here.

3.3 getters use

  • getters property

Getters is another attribute in the defineStore parameter configuration item. We talked about the state attribute earlier. The getter property value is an object, which contains various methods. You can think of getter as a computed property in Vue. Its function is to return a new result. Since it is similar to a computed property in Vue, it must be cached, just like computed.

Of course, our getter here is to process state data.

  • Add getter
    Let's take a look at how to define getter first, modify user.ts.

code show as below:

export const useUsersStore = defineStore("users", {
    
    
  state: () => {
    
    
    return {
    
    
      name: "小猪课堂",
      age: 25,
      sex: "男",
    };
  },
  getters: {
    
    
    getAddAge: (state) => {
    
    
      return state.age + 100;
    },
  },
});

In the above code, we added a getter attribute to the configuration item parameter, and a getAddAge method is defined in the attribute object. This method will receive a state parameter by default, which is the state object, and then the method returns a new data.

  • Using getters,
    we define getters in store, so how to use them in components? It is very simple to use, we modify App.vue.

code show as below:

<template>
  <p>新年龄:{
    
    {
    
     store.getAddAge }}</p>
  <button @click="patchStore">批量修改数据</button>
</template>
<script setup lang="ts">
import {
    
     useUsersStore } from "../src/store/user";
const store = useUsersStore();
// 批量修改数据
const patchStore = () => {
    
    
  store.$patch({
    
    
    name: "张三",
    age: 100,
    sex: "女",
  });
};
</script>

In the above code, we directly used the store.gettAddAge method on the label, which can ensure responsiveness. In fact, the name and other attributes in our state can also be used directly on the label in this way, and can also maintain responsiveness.

When we click the batch modify data button, the new age field on the page will also change accordingly.

  • Calling other getters in the getter
    Our previous getAddAge method simply used the state method, but sometimes we need to call other getter methods in this getter method, how to call it at this time?

In fact, it is very simple, we can call this directly in the getter method, and this points to the store instance, so we can call other getters of course.

The sample code is as follows:

export const useUsersStore = defineStore("users", {
    
    
  state: () => {
    
    
    return {
    
    
      name: "小猪课堂",
      age: 25,
      sex: "男",
    };
  },
  getters: {
    
    
    getAddAge: (state) => {
    
    
      return state.age + 100;
    },
    getNameAndAge(): string {
    
    
      return this.name + this.getAddAge; // 调用其它getter
    },
  },
});

In the above code, we defined a getter function named getNameAndAge, and directly used this inside the function to obtain state data and call other getter functions.

Careful friends may find that we do not use the form of arrow function here, because we use this inside the function. I believe everyone knows the problem of this pointing to the arrow function! So here we don't take the form of an arrow function.

Then there is no change in the form of calling in the component, the code is as follows:

<p>调用其它getter:{
    
    {
    
     store.getNameAndAge }}</p>
  • Getter pass parameters
    Since the getter function has done some calculations or processing, we probably need to pass parameters to the getter function, but we said earlier that the getter function is equivalent to the calculated property of the store, which is similar to the calculated property of Vue, so we all know Calculated properties in Vue cannot directly pass parameters, so if our getter function here needs to accept parameters, it also needs to be processed.

Sample code:

export const useUsersStore = defineStore("users", {
    
    
  state: () => {
    
    
    return {
    
    
      name: "小猪课堂",
      age: 25,
      sex: "男",
    };
  },
  getters: {
    
    
    getAddAge: (state) => {
    
    
      return (num: number) => state.age + num;
    },
    getNameAndAge(): string {
    
    
      return this.name + this.getAddAge; // 调用其它getter
    },
  },
});

In the above code, our getter function getAddAge receives a parameter num. This way of writing actually has a concept of closure in it, which means that we return a new function as a whole and pass the state into the new function.

Next we use it in the component, the way is very simple, the code is as follows:

 <p>新年龄:{
    
    {
    
     store.getAddAge(1100) }}</p>

3.4 The use of actions

  • Actions attribute
    The state and getters attributes we mentioned above are mainly at the data level, and there is no specific business logic code. They are the same as the data data and computed calculation attributes in our component code.

Then, if we have business code, it is best to unload the actions attribute, which is similar to the methods in our component code, and is used to place some methods for processing business logic.

The action attribute value is also an object, which also stores various methods, including synchronous methods and asynchronous methods.

  • Adding actions
    We can try to add an actions method and modify user.ts.

code show as below:

export const useUsersStore = defineStore("users", {
    
    
  state: () => {
    
    
    return {
    
    
      name: "小猪课堂",
      age: 25,
      sex: "男",
    };
  },
  getters: {
    
    
    getAddAge: (state) => {
    
    
      return (num: number) => state.age + num;
    },
    getNameAndAge(): string {
    
    
      return this.name + this.getAddAge; // 调用其它getter
    },
  },
  actions: {
    
    
    saveName(name: string) {
    
    
      this.name = name;
    },
  },
});

In the above code, we defined a very simple actions method. In actual scenarios, this method can be any logic, such as sending requests, storing tokens, and so on. You can treat the actions method as an ordinary method. The special thing is that this inside the method points to the current store.

  • Using actions
    to use the methods in actions is also very simple, for example, we want to call this method in App.vue.

code show as below:

const saveName = () => {
    
    
  store.saveName("我是小猪");
};

We click the button and directly call the actions method in the store.

3.5 Summary of sample code

The codes in the previous chapters are incomplete, and the main code is posted. In this section, we will post all the codes used in this article for everyone to practice.

main.ts code:

import {
    
     createApp } from "vue";
import App from "./App.vue";
import {
    
     createPinia } from "pinia";
const pinia = createPinia();


const app = createApp(App);
app.use(pinia);
app.mount("#app");

user.ts code:

import {
    
     defineStore } from "pinia";
// 第一个参数是应用程序中 store 的唯一 id
export const useUsersStore = defineStore("users", {
    
    
  state: () => {
    
    
    return {
    
    
      name: "小猪课堂",
      age: 25,
      sex: "男",
    };
  },
  getters: {
    
    
    getAddAge: (state) => {
    
    
      return (num: number) => state.age + num;
    },
    getNameAndAge(): string {
    
    
      return this.name + this.getAddAge; // 调用其它getter
    },
  },
  actions: {
    
    
    saveName(name: string) {
    
    
      this.name = name;
    },
  },
});

App.vue code:

<template>
  <img alt="Vue logo" src="./assets/logo.png" />
  <p>姓名:{
    
    {
    
     name }}</p>
  <p>年龄:{
    
    {
    
     age }}</p>
  <p>性别:{
    
    {
    
     sex }}</p>
  <p>新年龄:{
    
    {
    
     store.getAddAge(1100) }}</p>
  <p>调用其它getter:{
    
    {
    
     store.getNameAndAge }}</p>
  <button @click="changeName">更改姓名</button>
  <button @click="reset">重置store</button>
  <button @click="patchStore">批量修改数据</button>
  <button @click="saveName">调用aciton</button>


  <!-- 子组件 -->
  <child></child>
</template>
<script setup lang="ts">
import child from "./child.vue";
import {
    
     useUsersStore } from "../src/store/user";
import {
    
     storeToRefs } from "pinia";
const store = useUsersStore();
const {
    
     name, age, sex } = storeToRefs(store);
const changeName = () => {
    
    
  store.name = "张三";
  console.log(store);
};
// 重置store
const reset = () => {
    
    
  store.$reset();
};
// 批量修改数据
const patchStore = () => {
    
    
  store.$patch({
    
    
    name: "张三",
    age: 100,
    sex: "女",
  });
};
// 调用actions方法
const saveName = () => {
    
    
  store.saveName("我是小猪");
};
</script>

child.vue code:

<template>
  <h1>我是child组件</h1>
  <p>姓名:{
    
    {
    
     name }}</p>
  <p>年龄:{
    
    {
    
     age }}</p>
  <p>性别:{
    
    {
    
     sex }}</p>
  <button @click="changeName">更改姓名</button>
</template>
<script setup lang="ts">
import {
    
     useUsersStore } from "../src/store/user";
import {
    
     storeToRefs } from 'pinia';
const store = useUsersStore();
const {
    
     name, age, sex } = storeToRefs(store);
const changeName = () => {
    
    
  store.name = "小猪课堂";
};
</script>

4. Summary

Pinia has very few knowledge points, and if you have a Vuex foundation, it is even easier to learn. In fact, what we should pay more attention to is its function idea. Have you noticed that everything in Vue3 seems to be represented by a function, and pinia also continues this idea.

Therefore, it is more important for everyone to understand the idea of ​​this kind of combined programming. pinia is nothing more than the following three major points:

state
getters
actions
Of course, this article only explains the basic use part, but it can meet most of the needs in actual work. If you are still interested in learning other features of pinia, such as plug-ins, subscriptions, etc., you can move to the official website

Guess you like

Origin blog.csdn.net/u014212540/article/details/129415776