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
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
- Vuex4 for Vue3
- Vuex3 for Vue2
The latest version of Pinia is 2.x
- Both Vue2 and Vue3 are supported
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 no
Mutations
Actions
supports synchronous and asynchronousNested structure without modules
- Pinia provides a flat structure by design, that is, each store is independent of each other, no one belongs to anyone, that is, flat, better code separation and no namespace. Of course you can also implicitly nest stores by importing one module in another, and even have circular dependencies of stores
Better
TypeScript
support- There is no need to create custom complex wrappers to support TypeScript everything is typed, and the API is designed in a way that uses TS type inference as much as possible
- No need to inject, import functions, call them, enjoy auto-completion, make our development more convenient
- No need to manually add the store, its modules are automatically registered by default
Both Vue2 and Vue3 are supported
- Apart from the initial installation and SSR configuration, the API used by both is the same
Support
Vue DevTools
- Timeline for tracking actions, mutations
- The module itself can be observed in the component that uses the module
- Support time-travel for easier debugging
- In Vue2, Pinia will use all the interfaces of Vuex, so they cannot be used together
- But the debugging tool support for Vue3 is not perfect, for example, there is no time-travel function
Module hot update
- Modify modules without reloading the page
- Any existing state is maintained during hot updates
- Supports the use of plugins to extend Pinia functionality
- Support server-side rendering
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
- Among them,
state
used to store the global state. It must be an arrow function. In order to avoid data state pollution caused by cross-request when rendering on the server side, it can only be a function, and arrow functions must be used for better TS type deduction. getters
is used to encapsulate computed properties, which have the function of cachingactions
is used to encapsulate business logic and modify state
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
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
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。