Pinia 基础 - Pinia Essentials
- 接着学 Pinia
- Ref: https://pinia.vuejs.org/
创建 pinia 实例
1 | import { createApp } from "vue"; |
创建 store
Store (如 Pinia) 是一个保存状态和业务逻辑的实体,并不与组件树绑定(它承载着全局状态),每个组件都可以读取和写入它。它有三个概念,state、getter 和 action,我们可以假设这些概念相当于组件中的 data、 computed 和 methods。
1 | import { defineStore } from "pinia"; |
其中 defineStore 的第一个参数是 store 的唯一
ID,是必须传入的,Pinia 将用它来连接 store 和
devtools。为了养成习惯性的用法,将返回的函数命名为
use... 是一个符合组合式函数风格的约定。
defineStore()
的第二个参数可接受两类值:Setup 函数或 Option
对象
Option Store
与 Vue 的选项式 API
类似,我们也可以传入一个带有 state、actions 与
getters 属性的 Option 对象
1 | export const useCounterStore = defineStore("counter", { |
你可以认为 state 是 store 的数据
(data),getters 是 store
的计算属性 (computed),而 actions 则是方法
(methods)。
Setup Store
我们可以传入一个函数,该函数定义了一些响应式属性和方法,并且返回一个带有我们想暴露出去的属性和方法的对象。
1 | export const useCounterStore = defineStore("counter", () => { |
ref()就是state属性computed()就是gettersfunction()就是actions
要让
pinia正确识别state,你必须在setup store中返回state的所有属性。这意味着,你不能在store中使用私有属性。
Setup store 比 Option Store
带来了更多的灵活性,因为你可以在一个 store
内创建侦听器,并自由地使用任何组合式函数。Setup store
也可以依赖于全局提供的属性,比如路由。任何应用层面提供的属性都可以在
store 中使用 inject()
访问,就像在组件中一样:
1 | import { inject } from "vue"; |
不要返回像
route或appProvided(上例中)之类的属性,因为它们不属于store,而且你可以在组件中直接用useRoute()和inject('appProvided')访问。
使用 Store
1 | <script setup> |
store是一个用reactive包装的对象,这意味着不需要在getters后面写.value。就像setup中的props一样,我们不能对它进行解构- 为了从
store中提取属性时保持其响应性,你需要使用storeToRefs()。它将为每一个响应式属性创建引用。同时,你可以直接从store中解构action,因为它们也被绑定到store上:
1 | <script setup> |
State
重置 state
在 Setup Stores 中,您需要创建自己的
$reset() 方法:
1 | export const useCounterStore = defineStore("counter", () => { |
变更 state
除了用 store.count++ 直接改变
store,你还可以调用 $patch
方法。它允许你用一个 state
的补丁对象在同一时间更改多个属性:
1 | store.$patch({ |
$patch
方法也接受一个函数来组合难以用补丁对象实现的变更:
1 | store.$patch((state) => { |
替换 state
不能完全替换掉 store 的
state,因为那样会破坏其响应性。但是可以 patch
它。
1 | // 这实际上并没有替换`$state` |
订阅 state
可以通过 store 的 $subscribe() 方法侦听
state 及其变化。比起普通的 watch(),使用
$subscribe() 的好处是 subscriptions 在
patch 后只触发一次
1 | cartStore.$subscribe((mutation, state) => { |
刷新时机
在底层实现上,$subscribe() 使用了 Vue 的
watch() 函数。你可以传入与 watch()
相同的选项:
1 | cartStore.$subscribe( |
取消订阅
默认情况下,state subscription
会被绑定到添加它们的组件上 (如果 store 在组件的
setup()
里面)。这意味着,当该组件被卸载时,它们将被自动删除。如果你想在组件卸载后依旧保留它们,请将
{ detached: true } 作为第二个参数,以将
state subscription 从当前组件中分离:
1 | <script setup> |
可以在
pinia实例上使用 watch() 函数侦听整个state。
1 | watch( |
Getter
对于 Option Store 需要写 getter,但对于
Setup Store,你可以直接使用 computed() 来创建
getter。
1 | export const useCounterStore = defineStore('counter', { |
通过 this 可以访问 store 内的其他 getter 和 state。
使用 setup() 时的用法
1 | <script setup> |
使用选项式 API 的用法
1 | import { mapState } from "pinia"; |
Action
Action 相当于组件中的 method。它们可以通过
defineStore() 中的 actions
属性来定义,并且它们也是定义业务逻辑的完美选择。
1 | export const useCounterStore = defineStore("main", { |
类似 getter,action 也可通过
this 访问整个 store
实例,并支持完整的类型标注(以及自动补全✨)。不同的是,action
可以是异步的,你可以在它们里面 await 调用任何
API,以及其他 action!下面是一个使用
Mande
的例子。请注意,你使用什么库并不重要,只要你得到的是一个Promise。你甚至可以
(在浏览器中) 使用原生 fetch 函数:
1 | import { mande } from "mande"; |
使用 action
你可以使用 mapActions() 辅助函数将 action
属性映射为你组件中的方法。
1 | <script> |
订阅 action
你可以通过 store.$onAction() 来监听 action
和它们的结果。传递给它的回调函数会在 action
本身之前执行。after 表示在 promise
解决之后,允许你在 action
解决后执行一个回调函数。同样地,onError 允许你在
action 抛出错误或 reject
时执行一个回调函数。
1 | const unsubscribe = someStore.$onAction( |
默认情况下,action 订阅器会被绑定到添加它们的组件上(如果
store 在组件的 setup()
内)。这意味着,当该组件被卸载时,它们将被自动删除。如果你想在组件卸载后依旧保留它们,请将
true 作为第二个参数传递给 action
订阅器,以便将其从当前组件中分离:
1 | <script setup> |
在组件外使用 store
Pinia store 依靠 pinia
实例在所有调用中共享同一个 store
实例。大多数时候,只需调用你定义的 useStore()
函数,完全开箱即用。例如,在 setup()
中,你不需要再做任何事情。但在组件之外,情况就有点不同了。
实际上,useStore() 给你的 app 自动注入了
pinia 实例。这意味着,如果 pinia
实例不能自动注入,你必须手动提供给 useStore() 函数。
你可以根据不同的应用,以不同的方式解决这个问题。
1 | import { createRouter } from 'vue-router' |