13
头图

Vuex, as an old-fashioned Vue state management library, is familiar to everyone

Pinia is a brand new state management library specially developed for Vue by Vue.js team members, and has been included in the official github

Why do you need to develop another Pinia when you have Vuex?

Let's take a picture first, and look at the proposal for Vuex5 at that time, which is what the next generation of Vuex5 should look like

微信图片_20220314212501.png

Pinia is completely in line with the function points mentioned in his Vuex5 proposal at that time, so it can be said that Pinia is Vuex5, because its author is the official developer, and has been officially taken over, but currently Vuex and Pinia It is still two independent warehouses, which may be merged or developed independently in the future, but the official recommendation is Pinia.

Because the use of Vuex in Vue3 requires the use of Vuex4, and it can only be used as a transitional choice, there is a big flaw, so after the birth of the Composition API, a new state management Pinia was designed.

Pinia and Vuex

Vuex : State , Gettes , Mutations (sync), Actions (async)

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

The latest version of Vuex is 4.x

The latest version of Pinia is 2.x

At present, Pinia is much better than Vuex, and it solves many problems of Vuex, so I also highly recommend using Pinia directly, especially for TypeScript projects

Pinia core features

Pinia uses

Take Vue3 + TypeScript as an example

Install

npm install pinia

main.ts Initialize configuration

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

Create a user.ts in the store directory as an example, we first define and export a module named user

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

defineStore receives two parameters

The first parameter is the name of the module, which must be unique. Multiple modules cannot have the same name. Pinia will mount all modules to the root container.
The second parameter is an object with options similar to Vuex

access state

For example, we want to access the attribute count in state in the page

Since defineStore will return a function, you must first call to get the data object, and then you can 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()
// 解构
// const { count } = userStore()
</script>

For example, there is no problem with deconstructing and using it in the comments. Just pay attention. The data in this way is not responsive . If you want to deconstruct and keep responsive, you need to use a method storeToRefs() , the example is as follows

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

The reason is that Pinia actually processes the state data by reactive , which is the same as Vue3's reactive. The deconstructed one is not responsive, so it is necessary to do ref responsive proxy.

getters

This, like Vuex's getters, also has a cache function. It is used multiple times in the page as follows, the getters will be called for the first time, and the cache will be read after the data has not changed.

<template>
    <div>{{ myCount }}</div>
    <div>{{ myCount }}</div>
    <div>{{ myCount }}</div>
</template>

Note the difference between the two methods, written in the comments

getters: {
    // 方法一,接收一个可选参数 state
    myCount(state){
        console.log('调用了') // 页面中使用了三次,这里只会执行一次,然后缓存起来了
        return state.count + 1
    },
    // 方法二,不传参数,使用 this
    // 但是必须指定函数返回值的类型,否则类型推导不出来
    myCount(): number{
        return this.count + 1
    }
}

Updates and actions

There are four ways to update the data in the state. Let's look at three simple updates first. The instructions are written in the comments.

<template>
    <div>{{ user_store.count }}</div>
    <button @click="handleClick">按钮</button>
</template>
<script lang="ts" setup>
import { userStore } from '../store'
const user_store = userStore()
const handleClick = () => {
    // 方法一
    user_store.count++
    
    // 方法二,需要修改多个数据,建议用 $patch 批量更新,传入一个对象
    user_store.$patch({
        count: user_store.count1++,
        // arr: user_store.arr.push(1) // 错误
        arr: [ ...user_store.arr, 1 ] // 可以,但是还得把整个数组都拿出来解构,就没必要
    })
    
    // 使用 $patch 性能更优,因为多个数据更新只会更新一次视图
    
    // 方法三,还是$patch,传入函数,第一个参数就是 state
    user_store.$patch( state => {
        state.count++
        state.arr.push(1)
    })
}
</script>

The fourth method is that when there is a lot of logic or requests, we can encapsulate it into the actions in store/user.ts in the example

You can pass parameters, or you can directly get the data in the state through this.xx. It should be noted that actions cannot be defined with arrow functions, otherwise the external this will be bound.

actions: {
    changeState(num: number){ // 不能用箭头函数
        this.count += num
    }
}

transfer

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

Support VueDevtools

Open the developer tool Vue Devtools and you will find Pinia, and you can manually modify the data for debugging, which is very convenient

image.png

Mock call interface

Example:

We first define the example interface api/user.ts

// 接口数据类型
export interface userListType{
    id: number
    name: string
    age: number
}
// 模拟请求接口返回的数据
const userList = [
    { id: 1, name: '张三', age: 18 },
    { id: 2, name: '李四', age: 19 },
]
// 封装模拟异步效果的定时器
async function wait(delay: number){
    return new Promise((resolve) => setTimeout(resolve, delay))
}
// 接口
export const getUserList = async () => {
    await wait(100) // 延迟100毫秒返回
    return userList
}

Then encapsulate the calling interface in actions in store/user.ts

import { defineStore } from 'pinia'
import { getUserList, userListType } from '../api/user'
export const userStore = defineStore('user', {
    state: () => {
        return {
            // 用户列表
            list: [] as userListType // 类型转换成 userListType
        }
    },
    actions: { 
        async loadUserList(){
            const list = await getUserList()
            this.list = list
        }
    }
})

Call actions in the page 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() // 加载所有数据
</script>

Modify data across modules

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

Example: For example, modify the name of a user in the user module in the chat module

// chat.ts
import { defineStore } from 'pinia'
import { userStore } from './user'
export const chatStore = defineStore('chat', {
    actions: { 
        someMethod(userItem){
            userItem.name = '新的名字'
            const user_store = userStore()
            user_store.updateUserName(userItem)
        }
    }
})

user 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
            }
        }
    }
})

Epilogue

If this article is a little bit helpful to you, please give it a like and support. Every [like] of yours is the biggest motivation for my creation, thank you for your support ^_^

Scan the code and follow the official account, you can add me as a friend, I will pull you into the front-end communication group, everyone can communicate and make progress together

WechatIMG754.jpg


沐华
175 声望18 粉丝