Pinia state management

1. Comparison between Pinia and Vuex

1.1. What is Pinia?

Pinia (pronounced /piːnjʌ/, like "peenya" in English) is the closest word to piña (pineapple in Spanish);

  • Pinia started around 2019, initially as an experiment to redesign state management for Vue so that it works like a Composition API.
  • From then to now, the original design principles are still the same, and it is currently compatible with Vue2 and Vue3, and does not require you to use the Composition API;
  • Pinia is still essentially a state management library for state sharing across components and pages (this is the same as Vuex and Redux);

1.2 The difference between Pinia and Vuex 

So don't we already have Vuex? Why use Pinia?

  • Pinia was originally created to explore what the next iteration of Vuex would look like, incorporating many ideas from the Vuex 5 core team discussions;
  • In the end, the team realized that Pinia has implemented most of the content in Vuex5 , so it finally decided to replace Vuex with Pinia ;
  • Compared with Vuex, Pinia provides a simpler API with less ceremony , providing a Composition-API style API;
  • Most importantly, has solid type inference support when used with TypeScript ;

Compared with Vuex, Pinia has many advantages:

  • For example mutations no longer exist:
    • They are often considered very verbose ;
    • They originally brought devtools integration, but this is no longer an issue;
  • More friendly TypeScript support, Vuex's previous support for TS was very unfriendly ;
  • No more nested structures of modules :
    • You can use each store flexibly, and they use each other in a flat manner;
  • There is no longer the concept of namespaces, and there is no need to remember their complex relationships ;

1.3. How to use Pinia? 

Before using Pinia, we need to install it first:

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

Create a pinia and pass it to the application:

 

2. Create a Pinia Store 

2.1. Understanding Store

What is a store?

  • A Store (such as Pinia) is an entity that holds the state and business logic bound to your component tree , that is, holds the global state;
  • It's kind of like a component that's always there and everyone can read and write to it ;
  • You can define any number of Stores in your application to manage your state;

Store has three core concepts:

  • state、getters、actions
  • Equivalent to data, computed, methods of components;
  • Once the store is instantiated, you can directly access any properties defined in state, getters and actions on the store ;

2.2, define a Store

Define a Store:

  • We need to know that Store is defined using defineStore() ,
  • And it requires a unique name , passed as the first parameter;

This name, also known as id, is required and used by Pinia to connect the store to devtools.

The returned functions uniformly use useX as the naming scheme, which is the agreed specification;

2.3, using the defined Store

Store will not be created until it is used, we can use Store by calling the use function :

Note: After the Store is obtained, it cannot be deconstructed, and the responsiveness will be lost :

  • In order to extract properties from the Store while keeping it responsive, you need to use storeToRefs()

2.4. Example code 

index.js

import {createPinia} from 'pinia'
// 创建pinia
const pinia = createPinia()
export default pinia

counter.js

// 定义关于counter的store
import {defineStore} from 'pinia'

// 参数一为标识名
// 返回值为一个函数
const useCounter = defineStore("counter", {
    state: () => ({
        count: 99
    })
})

export default useCounter

Home.vue

<template>
  <div class="home">
    <h2>Home View</h2>
    <h2>count: {
   
   { counterStore.count }}</h2>
    <h2>count: {
   
   { count }}</h2>
    <button @click="incrementCount">count+1</button>
  </div>
</template>

<script setup>
  import {toRefs} from 'vue'
  import {storeToRefs} from 'pinia'
  import useCounter from '@/stores/counter';

  // 调用函数,拿到store对象
  const counterStore = useCounter()

  // 解构对象(解构出来的对象会失去响应式)
  // const { count } = toRefs(counterStore)
  // storeToRefs这是vue提供的,作用与toRefs相同
  const {count} = storeToRefs(counterStore)


  // 修改数据
  function incrementCount() {
    counterStore.count++
  }

</script>

<style scoped>
</style>

app.vue

<template>
  <div class="app">
    <h2>App Component</h2>
    <hr>
    <home/>
  </div>
</template>

<script setup>

  import Home from './views/Home.vue'

</script>

<style>

</style>

main.js

import {createApp} from 'vue'
import App from './App.vue'
import pinia from './stores/index.js'

createApp(App).use(pinia).mount('#app')

Note: index.js, App.vue, and main.js will not change in the future, so the following sample code will not be written out.

3. The core concept of Pinia State

3.1. Understanding and defining State

State is the core part of store, because store is used to help us manage state.

  • In Pinia, states are defined as functions that return the initial state;

3.2. Operating State (1) 

Read and write state:

  • By default, you can directly read and write state by accessing it through the store instance;

Reset state:

  • You can reset the state to its initial value  by calling the $reset() method on the store ;

3.3. Operating State (2) 

Change State:

  • In addition to modifying the store directly with store.counter++, you can also call the $patch method;
  • It allows you to apply multiple changes at the same time using partial "state" objects ;

Replace State:

  • You can replace the entire state of a Store by setting its $state property to a new object: 

3.4. Code example 

Home.vue

<template>
  <div class="home">
    <h2>Home View</h2>
    <h2>name: {
   
   { name }}</h2>
    <h2>age: {
   
   { age }}</h2>
    <h2>level: {
   
   { level }}</h2>
    <button @click="changeState">修改state</button>
    <button @click="resetState">重置state</button>
  </div>
</template>

<script setup>
  import useUser from '@/stores/user'
  import {storeToRefs} from 'pinia';

  const userStore = useUser()
  const {name, age, level} = storeToRefs(userStore)

  function changeState() {
    // 1.一个个修改状态
    // userStore.name = "kobe"
    // userStore.age = 20
    // userStore.level = 200

    // 2.一次性修改多个状态
    // userStore.$patch({
    //   name: "james",
    //   age: 35
    // })

    // 3.替换state为新的对象
    const oldState = userStore.$state
    userStore.$state = {
      name: "curry",
      level: 200
    }
    console.log(oldState === userStore.$state)
  }

  function resetState() {
    userStore.$reset() // 重置state
  }

</script>

<style scoped>
</style>

user.js

import {defineStore} from 'pinia'

const useUser = defineStore("user", {
    state: () => ({
        name: "why",
        age: 18,
        level: 100
    })
})

export default useUser

4. Pinia core concept Getters

4.1. Understanding and defining Getters

Getters are equivalent to Store's computed properties:

  • They can be defined with the getters property in defineStore() ;
  • Getters can define functions that accept a state as a parameter ;

4.2. Access Getters (1) 

Getters for accessing the current store:

Access your own other Getters in Getters:

  • We can access all other properties of the current store instance through this; 

Getters for accessing other stores:

4.3. Access Getters (2) 

Getters can also return a function, so that they can accept parameters:

4.4. Code example 

counter.js

// 定义关于counter的store
import {defineStore} from 'pinia'

import useUser from './user.js'

const useCounter = defineStore("counter", {
    state: () => ({
        count: 99,
        friends: [
            {id: 111, name: "why"},
            {id: 112, name: "kobe"},
            {id: 113, name: "james"},
        ]
    }),
    getters: {
        // 1.基本使用
        doubleCount(state) {
            return state.count * 2
        },
        // 2.一个getter引入另外一个getter
        doubleCountAddOne() {
            // this是store实例
            return this.doubleCount + 1
        },
        // 3.getters也支持返回一个函数
        getFriendById(state) {
            return function (id) {
                for (let i = 0; i < state.friends.length; i++) {
                    const friend = state.friends[i]
                    if (friend.id === id) {
                        return friend
                    }
                }
            }
        },
        // 4.getters中用到别的store中的数据
        showMessage(state) {
            // 1.获取user信息
            const userStore = useUser()

            // 2.获取自己的信息

            // 3.拼接信息
            return `name:${userStore.name}-count:${state.count}`
        }
    }
})

export default useCounter

Home.vue 

<template>
  <div class="home">
    <h2>Home View</h2>
    <h2>doubleCount: {
   
   { counterStore.doubleCount }}</h2>
    <h2>doubleCountAddOne: {
   
   { counterStore.doubleCountAddOne }}</h2>
    <h2>friend-111: {
   
   { counterStore.getFriendById(111) }}</h2>
    <h2>friend-112: {
   
   { counterStore.getFriendById(112) }}</h2>
    <h2>showMessage: {
   
   { counterStore.showMessage }}</h2>
    <button @click="changeState">修改state</button>
    <button @click="resetState">重置state</button>
  </div>
</template>

<script setup>
  import useCounter from '@/stores/counter';

  const counterStore = useCounter()

</script>

<style scoped>
</style>

5. Pinia core concept Actions

5.1. Understanding and defining Actions

Actions are equivalent to methods in components.

  • can be defined using the actions attribute in defineStore() , and they are very suitable for defining business logic;

Like getters, all operations of  the entire store instance can be accessed through this in the action ;

5.2, Actions perform asynchronous operations 

And Actions supports asynchronous operations, and we can write asynchronous functions and use await in the functions;

5.3. Code example 

counter.js 

// 定义关于counter的store
import {defineStore} from 'pinia'

import useUser from './user'

const useCounter = defineStore("counter", {
    state: () => ({
        count: 99,
        friends: [
            {id: 111, name: "why"},
            {id: 112, name: "kobe"},
            {id: 113, name: "james"},
        ]
    }),
    getters: {
        // 1.基本使用
        doubleCount(state) {
            return state.count * 2
        },
        // 2.一个getter引入另外一个getter
        doubleCountAddOne() {
            // this是store实例
            return this.doubleCount + 1
        },
        // 3.getters也支持返回一个函数
        getFriendById(state) {
            return function (id) {
                for (let i = 0; i < state.friends.length; i++) {
                    const friend = state.friends[i]
                    if (friend.id === id) {
                        return friend
                    }
                }
            }
        },
        // 4.getters中用到别的store中的数据
        showMessage(state) {
            // 1.获取user信息
            const userStore = useUser()

            // 2.获取自己的信息

            // 3.拼接信息
            return `name:${userStore.name}-count:${state.count}`
        }
    },
    actions: {
        increment() {
            this.count++
        },
        incrementNum(num) {
            this.count += num
        }
    }
})

export default useCounter

home.js

import {defineStore} from 'pinia'

const useHome = defineStore("home", {
    state: () => ({
        banners: [],
        recommends: []
    }),
    actions: {
        async fetchHomeMultidata() {
            // fetchHomeMultidata() {
            const res = await fetch("http://123.207.32.32:8000/home/multidata")
            const data = await res.json()

            this.banners = data.data.banner.list
            this.recommends = data.data.recommend.list

            // return new Promise(async (resolve, reject) => {
            //   const res = await fetch("http://123.207.32.32:8000/home/multidata")
            //   const data = await res.json()

            //   this.banners = data.data.banner.list
            //   this.recommends = data.data.recommend.list

            //   resolve("bbb")
            // })
        }
    }
})

export default useHome

Home.vue

<template>
  <div class="home">
    <h2>Home View</h2>
    <h2>doubleCount: {
   
   { counterStore.count }}</h2>
    <button @click="changeState">修改state</button>

    <!-- 展示数据 -->
    <h2>轮播的数据</h2>
    <ul>
      <template v-for="item in homeStore.banners">
        <li>{
   
   { item.title }}</li>
      </template>
    </ul>
  </div>
</template>

<script setup>
  import useCounter from '@/stores/counter';
  import useHome from '@/stores/home';

  const counterStore = useCounter()

  function changeState() {
    // counterStore.increment()
    counterStore.incrementNum(10)
  }

  const homeStore = useHome()
  homeStore.fetchHomeMultidata().then(res => {
    console.log("fetchHomeMultidata的action已经完成了:", res)
  })

</script>

<style scoped>
</style>

Guess you like

Origin blog.csdn.net/weixin_52851967/article/details/128764471