基本使用
这就是使用 pinia 在 API 方面的样子(请务必查看 Getting Started 以获取完整说明)。 您首先创建一个 Store :
// stores/counter.js
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => {
return { count: 0 }
},
// 也可以定义为
// state: () => ({ count: 0 })
actions: {
increment() {
this.count++
},
},
getters:{
}
})
然后你在一个组件中 使用 它:
import { useCounterStore } from '@/stores/counter'
export default {
setup() {
const counter = useCounterStore()
counter.count++
// 带自动补全 ✨
counter.$patch({ count: counter.count + 1 })
// 或使用 action 代替
counter.increment()
},
}
其他方法查看官方文档
安装
yarn add pinia
# 或者使用 npm
npm install pinia
如果您的应用使用 Vue 2,您还需要安装组合 API:@vue/composition-api。 如果您使用 Nuxt,则应遵循 这些说明。
创建一个 pinia(根存储)并将其传递给应用程序:
import { createPinia } from 'pinia'
app.use(createPinia())
如果您使用的是 Vue 2,您还需要安装一个插件并将创建的 pinia 注入应用程序的根目录:
import { createPinia, PiniaVuePlugin } from 'pinia'
Vue.use(PiniaVuePlugin)
const pinia = createPinia()
new Vue({
el: '#app',
// 其他选项...
// ...
// 注意同一个 `pinia` 实例可以在多个 Vue 应用程序中使用
// 同一个页面
pinia,
})
Store
它托管全局状态。(集中式状态管理)它有点像一个始终存在并且每个人都可以读取和写入的组件。它有三个概念,state、getters 和 actions 并且可以安全地假设这些概念等同于组件中的“数据”、“计算”和“方法”。
存储应该包含可以在整个应用程序中访问的数据。
定义一个Store
import { defineStore } from 'pinia'
// useStore 可以是 useUser、useCart 之类的任何东西
// 第一个参数是应用程序中 store 的唯一 id ,Pinia 使用它来将 store 连接到 devtools。
export const useStore = defineStore('main', {
state: () => {
return { count: 0 }
},
// 也可以定义为
// state: () => ({ count: 0 })
actions: {
increment() {
this.count++
},
},
getters:{
}
})
这个 name,也称为 id,是必要的,Pinia 使用它来将 store 连接到 devtools。 将返回的函数命名为 use... 是跨可组合项的约定,以使其符合你的使用习惯。
使用Store
import { useStore } from '@/stores/counter'
export default {
setup() {
const store = useStore()
return {
// 您可以返回整个 store 实例以在模板中使用它
store,
}
},
}
注意:
Store中得类型不支持解构
const store = useStore()
const { name, doubleCount } = store // ❌ 这不起作用,因为它会破坏响应式
const { name, doubleCount } = storeToRefs(store) // ❎ 通过storeToRefs()解构可以保持响应式
访问Store
const store = useStore()
store.counter++
重置Store
const store = useStore()
store.$reset()
组合式API使用
// ./src/stores/counterStore.js
import { defineStore } from 'pinia',
const useCounterStore = defineStore('counterStore', {
state: () => ({
counter: 0
})
})
import { useCounterStore } from '../stores/counterStore'
export default {
setup() {
const counterStore = useCounterStore()
return { counterStore }
},
computed: {
tripleCounter() {
return counterStore.counter * 3
},
},
}
选项式API使用
如果您不使用 Composition API,并且使用的是 computed、methods、...,则可以使用 mapState() 帮助器将状态属性映射为只读计算属性:
import { mapState } from 'pinia'
import { useCounterStore } from '../stores/counterStore'
export default {
computed: {
// 允许访问组件内部的 this.counter
// 与从 store.counter 读取相同
...mapState(useCounterStore, {
myOwnName: 'counter',
// 您还可以编写一个访问 store 的函数
double: store => store.counter * 2,
// 它可以正常读取“this”,但无法正常写入...
magicValue(store) {
return store.someGetter + this.counter + this.double
},
}),
},
}
如果您希望能够写入这些状态属性(例如,如果您有一个表单),您可以使用 mapWritableState() 代替。 请注意,您不能传递类似于 mapState() 的函数:
import { mapWritableState } from 'pinia'
import { useCounterStore } from '../stores/counterStore'
export default {
computed: {
// 允许访问组件内的 this.counter 并允许设置它
// this.counter++
// 与从 store.counter 读取相同
...mapWritableState(useCounterStore, ['counter'])
// 与上面相同,但将其注册为 this.myOwnName
...mapWritableState(useCounterStore, {
myOwnName: 'counter',
}),
},
}
改变状态
除了直接用 store.counter++ 修改 store,你还可以调用 $patch 方法。 它允许您使用部分“state”对象同时应用多个更改:
store.$patch({
counter: store.counter + 1,
name: 'Abalam',
})
但是,使用这种语法应用某些突变非常困难或代价高昂:任何集合修改(例如,从数组中推送、删除、拼接元素)都需要您创建一个新集合。 正因为如此,$patch 方法也接受一个函数来批量修改集合内部分对象的情况:
cartStore.$patch((state) => {
state.items.push({ name: 'shoes', quantity: 1 })
state.hasChanged = true
})
这里的主要区别是$patch() 允许您将批量更改的日志写入开发工具中的一个条目中。 注意两者,state 和 $patch() 的直接更改都出现在 devtools 中,并且可以进行 time travelled(在 Vue 3 中还没有)。
替换state
您可以通过将其 $state 属性设置为新对象来替换 Store 的整个状态:
store.$state = { counter: 666, name: 'Paimon' }
您还可以通过更改 pinia 实例的 state 来替换应用程序的整个状态。 这在 SSR for hydration 期间使用。
pinia.state.value = {}
订阅状态
……
Getters
方法一:箭头函数
export const useStore = defineStore('main', {
state: () => ({
counter: 0,
}),
getters: {
doubleCount: (state) => state.counter * 2,
},
})
方法二:常规函数
export const useStore = defineStore('main', {
state: () => ({
counter: 0,
}),
getters: {
// 自动将返回类型推断为数字
doubleCount(state) {
return state.counter * 2
},
// 返回类型必须明确设置
//我们可以在定义常规函数时通过 this 访问到 整个 store 的实例, 但是需要定义返回类型(在 TypeScript 中)。
doublePlusOne(): number {
return this.counter * 2 + 1
},
},
})
使用getter
<template>
<p>Double count is {{ store.doubleCount }}</p>
</template>
<script>
export default {
setup() {
const store = useStore()
return { store }
},
}
</script>
getter可以接受参数
可以访问其他仓库中得getter
与 setup() 一起使用
您可以直接访问任何 getter 作为 store 的属性(与 state 属性完全一样):
export default {
setup() {
const store = useStore()
store.counter = 3
store.doubleCount // 6
},
}
使用选项式API
Actions
Actions 相当于组件中的 methods。 它们可以使用 defineStore() 中的 actions 属性定义,并且它们非常适合定义业务逻辑:
actions 可以是异步的
操作可以通过 this 访问 whole store instance
export const useStore = defineStore('main', {
state: () => ({
counter: 0,
}),
actions: {
increment() {
this.counter++
},
randomizeCounter() {
this.counter = Math.round(100 * Math.random())
},
},
})
import { mande } from 'mande'
const api = mande('/api/users')
export const useUsers = defineStore('users', {
state: () => ({
userData: null,
// ...
}),
actions: {
async registerUser(login, password) {
try {
this.userData = await api.post({ login, password })
showTooltip(`Welcome back ${this.userData.name}!`)
} catch (error) {
showTooltip(error)
// 让表单组件显示错误
return error
}
},
},
})
组件中调用actions
const main = useMainStore()
// Actions 像 methods 一样被调用:
main.randomizeCounter()
可以访问其他仓库中得getter
与 setup() 一起使用
您可以直接调用任何操作作为 store 的方法:
export default {
setup() {
const store = useStore()
store.randomizeCounter()
},
}
使用选项API
Plugins
……