1.安装

1
2
3
yarn add pinia
# 或者使用 npm
npm install pinia

在vue项目中,一般会在src下建立/store文件夹,用以存放全局状态文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
// src/store/index.ts
import {createPinia} from "pinia";
const store = createPinia();
export default store;


// main.ts
import store from './store'
app.use(store)
//------------------------------------------------
//或直接在main.ts中引入
import { createPinia } from 'pinia'
app.use(createPinia())

2.核心概念

1.State

大多数时候,state 是 store 的核心部分。 我们通常从定义应用程序的状态开始。 在 Pinia 中,状态被定义为返回初始状态的函数。 Pinia 在服务器端和客户端都可以工作。(其实也就是全局共享的数据,如登录用户信息)

定义State

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import { defineStore } from 'pinia'
const useStore = defineStore('storeId', {
// 推荐使用 完整类型推断的箭头函数
state: () => {
return {
// 所有这些属性都将自动推断其类型
counter: 0,
name: 'Eduardo',
isAdmin: true,
}
},
})

//或者
import { defineStore } from 'pinia'
const useStore = defineStore('storeId', {
// 推荐使用 完整类型推断的箭头函数
state: () => ({
// 所有这些属性都将自动推断其类型
counter: 0,
name: 'Eduardo',
isAdmin: true,
}),
})

当你在箭头函数中使用大括号 {} 包围代码块时,你必须显式使用 return 语句来指定返回值。这是因为大括号 {} 可以包含多个语句,因此你需要明确指定哪个语句的结果应该作为函数的返回值。
所以,如果你写成 f: () => { return {} },这与 f: () => ({}) 是等价的,都表示函数 f 返回一个空对象。在这种情况下,大括号 {} 包含了一个 return 语句,指定了返回值为空对象。如果你省略大括号,那么箭头函数将默认将后面的表达式作为返回值。

访问State

1
2
3
4
5
6
import {useStore} from '../store/module/user'
const store = useStore()
//访问状态中的属性
console.log(store.counter)
//重置到初始状态
store.$reset()

注意store类型是一个reactive包裹的响应式对象

为了从 Store 中提取属性同时保持其响应式,您需要使用storeToRefs()或者使用计算属性。 它将为任何响应式属性创建 refs。 当您仅使用 store 中的状态但不调用任何操作时,这很有用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { storeToRefs } from 'pinia'
export default defineComponent({
setup() {
const store = useStore()
// `name` 和 `doubleCount` 是响应式引用
// 这也会为插件添加的属性创建引用
// 但跳过任何 action 或 非响应式(不是 ref/reactive)的属性
const { name, doubleCount } = storeToRefs(store)
//doubleValue: computed(() => store.doubleCount),
return {
name,
doubleCount
}
},
})

修改状态

方式1

1
store.counter++;

方式2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//需要重新赋一个新的值,不利于数组,集合的增删修改
store.$patch({
counter: store.counter + 1,
name: 'Abalam',
})

//函数形式,对state进行修改
store.$patch((state) => {
state.items.push({ name: 'shoes', quantity: 1 })
state.hasChanged = true
})

//直接修改state
store.$state = { counter: 666, name: 'Paimon' }

订阅状态

订阅状态的变化,每当状态被修改或改变时,都会触发回调函数

1
2
3
4
5
6
7
8
9
10
cartStore.$subscribe((mutation, state) => {
// import { MutationType } from 'pinia'
mutation.type // 'direct' 方式1| 'patch object' 方式2(1)| 'patch function' 方式2(2)
// 与 cartStore.$id 相同
mutation.storeId // 'cart'
// 仅适用于 mutation.type === 'patch object'
mutation.payload // 补丁对象传递给 to cartStore.$patch()
// 每当它发生变化时,将整个状态持久化到本地存储
localStorage.setItem('cart', JSON.stringify(state))
})

2.Getters

Getter 完全等同于 Store 状态的 计算值。 它们可以用 defineStore() 中的 getters 属性定义。 他们接收“状态”作为第一个参数以鼓励箭头函数的使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
export const useStore = defineStore('main', {
state: () => ({
counter: 0,
}),
getters: {
// 自动将返回类型推断为数字
doubleCount(state) {
return state.counter * 2
},
// 返回类型必须明确设置
doublePlusOne(): number {
return this.counter * 2 + 1
},
// 访问其他getters
doubleCountPlusOne():number {
// 自动完成 ✨
return this.doubleCount + 1
},
// 传递参数给getters
// getters本身和计算属性一样,无法接收参数,但可以返回一个接收参数的函数。如下面的getUserById
getUserById: (state) => {
return (userId) => state.users.find((user) => user.id === userId)
},
// 访问其他Store的getters
// 首先需要在顶部import其他Store
// import {useOtherStore} from './other-store'
getOtherStore(state){
const otherStore = useOtherStore()
return state.count + otherStore.count
},

},
})

可以直接访问Store的任何getter作为store的属性,就和state属性一样

3.Actions

Actions 相当于组件中的 methods。 它们可以使用 defineStore() 中的 actions 属性定义,并且它们非常适合定义业务逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
export const useStore = defineStore('main', {
state: () => ({
counter: 0,
}),
actions: {
increment() {
this.counter++
},
randomizeCounter() {
this.counter = Math.round(100 * Math.random())
},
},
})
  1. 和getters一样,在actions同样可以访问其他Store
  2. 可以定义异步方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { useAuthStore } from './auth-store'

export const useSettingsStore = defineStore('settings', {
state: () => ({
// ...
}),
actions: {
async fetchUserPreferences(preferences) {
const auth = useAuthStore()
if (auth.isAuthenticated) {
this.preferences = await fetchPreferences()
} else {
throw new Error('User must be authenticated')
}
},
},
})

订阅Actions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
const unsubscribe = someStore.$onAction(
({
name, // action 的名字
store, // store 实例
args, // 调用这个 action 的参数
after, // 在这个 action 执行完毕之后,执行这个函数
onError, // 在这个 action 抛出异常的时候,执行这个函数
}) => {
// 记录开始的时间变量
const startTime = Date.now()
// 这将在 `store` 上的操作执行之前触发
console.log(`Start "${name}" with params [${args.join(', ')}].`)

// 如果 action 成功并且完全运行后,after 将触发。
// 它将等待任何返回的 promise
after((result) => {
console.log(
`Finished "${name}" after ${
Date.now() - startTime
}ms.\nResult: ${result}.`
)
})

// 如果 action 抛出或返回 Promise.reject ,onError 将触发
onError((error) => {
console.warn(
`Failed "${name}" after ${Date.now() - startTime}ms.\nError: ${error}.`
)
})
}
)

// 手动移除订阅
unsubscribe()