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 no
Mutations
Actions
Support 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
- better
TypeScript
support- 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
- support
Vue 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 them
state
To 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 getters
It’s used to encapsulate computational properties, and it has caching capabilitiesactions
It 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