目录
简介
安装
创建pinia实例
Store
Option Store
Setup Store
使用Store
Store解构
State
访问State
通过store实例直接访问
重置State
选项式API通过$reset()方法将state重置为初始值
组合式Setup中,需要自己创建重置方法
修改State
直接修改
批量修改$patch
通过action修改
订阅state($subscribe)
Getter
Action
访问其他 store 的 action
订阅 action($onAction)
面试重点
Pinia 和 Vuex 的区别是什么?
Pinia 如何实现响应式?
如何优雅处理异步操作的错误?
在组件外使用 Pinia Store得应用场景
符合直觉的 Vue.js 状态管理库,类型安全、可扩展性以及模块化设计。 甚至让你忘记正在使用的是一个状态库。
yarn add pinia
# 或者使用 npm
npm install pinia
src/main.ts
中创建并使用pinia
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
const pinia = createPinia()
const app = createApp(App)
app.use(pinia)
app.mount('#app')
Store (如 Pinia) 是一个保存状态和业务逻辑的实体,它并不与你的组件树绑定。换句话说,它承载着全局状态。它有点像一个永远存在的组件,每个组件都可以读取和写入它。它有三个概念,state,getter和 action,这些概念相当于组件中的
data
、computed
和methods
。只有整个应用都要访问的数据才适合使用store,不要滥用strore
也可以传入一个带有
state
、actions
与getters
属性的 Option 对象
export const useCounterStore = defineStore('counter', {
state: () => ({ count: 0, name: 'Eduardo' }),
getters: {
doubleCount: (state) => state.count * 2,
},
actions: {
increment() {
this.count++
},
},
})
与 Vue 组合式 API 的 setup 函数相似,我们可以传入一个函数,该函数定义了一些响应式属性和方法,并且返回一个带有我们想暴露出去的属性和方法的对象。
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
const doubleCount = computed(() => count.value * 2)
function increment() {
count.value++
}
return { count, doubleCount, increment }
})
ref()
就是 state
属性computed()
就是 getters
function()
就是 actions
注意: 要让 pinia 正确识别 state
,你必须在 setup store 中返回 state
的所有属性。这意味着,你不能在 store 中使用私有属性。
使用
调用
useStore()
(或者使用setup()
函数,像所有的组件那样) 之前,store 实例是不会被创建的
使用storeToRefs()对Store提取属性并保持响应式,和toRefs的区别在于storeToRefs会跳过所有的action以及非响应式的属性
在Option Store中state是一个返回初始状态的函数,在setup Store中,你定义的每一项数据都是state
import { defineStore } from 'pinia'
const useStore = defineStore('storeId', {
// 为了完整类型推理,推荐使用箭头函数
state: () => {
return {
// 所有这些属性都将自动推断出它们的类型
count: 0,
name: 'Eduardo',
isAdmin: true,
items: [],
hasChanged: true,
}
},
})
const store = useStore()
store.count++
const store = useStore()
store.$reset()
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
function $reset() {
count.value = 0
}
return { count, $reset }
})
countStore.sum = 666
countStore.$patch({
sum:999,
name:'测试'
})
import { defineStore } from 'pinia'
export const useCountStore = defineStore('count', {
actions: {
//加
increment(value:number) {
if (this.sum < 10) {
//操作countStore中的sum
this.sum += value
}
},
//减
decrement(value:number){
if(this.sum > 1){
this.sum -= value
}
}
},
})
组件中调用action
// 使用countStore
const countStore = useCountStore()
// 调用对应action
countStore.incrementOdd(n.value)
通过 store 的
$subscribe()
方法侦听 state 及其变化。比起普通的watch()
,使用$subscribe()
的好处是 subscriptions 在 patch 后只触发一次
cartStore.$subscribe((mutation, state) => {
// import { MutationType } from 'pinia'
mutation.type // 'direct' | 'patch object' | 'patch function'
// 和 cartStore.$id 一样
mutation.storeId // 'cart'
// 只有 mutation.type === 'patch object'的情况下才可用
mutation.payload // 传递给 cartStore.$patch() 的补丁对象。
// 每当状态发生变化时,将整个 state 持久化到本地存储。
localStorage.setItem('cart', JSON.stringify(state))
})
等同于 store 的 state 的计算值。可以通过
defineStore()
中的getters
属性来定义它们。推荐使用箭头函数,并且它将接收state
作为第一个参数(有点类似计算属性)
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0,
}),
getters: {
doubleCount: (state) => state.count * 2,
},
})
注意:可以通过 this
访问到整个 store 实例,但(在 TypeScript 中)必须定义返回类型。
相当于组件中的method。它们可以通过
defineStore()
中的actions
属性来定义,并且它们也是定义业务逻辑的完美选择。
export const useCounterStore = defineStore('main', {
state: () => ({
count: 0,
}),
actions: {
increment() {
this.count++
},
randomizeCounter() {
this.count = Math.round(100 * Math.random())
},
},
})
action可以是异步的,实际开发中可能通过async await 调用api接口之类的业务请求,并且可以通过this访问整个store实例
将其他store引入,然后得到对应引入的store
import { useAuthStore } from './auth-store'
export const useSettingsStore = defineStore('settings', {
state: () => ({
preferences: null,
// ...
}),
actions: {
async fetchUserPreferences() {
const auth = useAuthStore()
if (auth.isAuthenticated) {
this.preferences = await fetchPreferences()
} else {
throw new Error('User must be authenticated')
}
},
},
})
通过
store.$onAction()
来监听 action 和它们的结果。传递给它的回调函数会在 action 本身之前执行。after
表示在 promise 解决之后,允许你在 action 解决后执行一个回调函数。同样地,onError
允许你在 action 抛出错误或 reject 时执行一个回调函数。
const unsubscribe = someStore.$onAction(
({
name, // action 名称
store, // store 实例,类似 `someStore`
args, // 传递给 action 的参数数组
after, // 在 action 返回或解决后的钩子
onError, // action 抛出或拒绝的钩子
}) => {
// 为这个特定的 action 调用提供一个共享变量
const startTime = Date.now()
// 这将在执行 "store "的 action 之前触发。
console.log(`Start "${name}" with params [${args.join(', ')}].`)
// 这将在 action 成功并完全运行后触发。
// 它等待着任何返回的 promise
after((result) => {
console.log(
`Finished "${name}" after ${
Date.now() - startTime
}ms.\nResult: ${result}.`
)
})
// 如果 action 抛出或返回一个拒绝的 promise,这将触发
onError((error) => {
console.warn(
`Failed "${name}" after ${Date.now() - startTime}ms.\nError: ${error}.`
)
})
}
)
// 手动删除监听器
unsubscribe()
mutations
,直接通过 actions
同步/异步修改状态Composition API
设计,天然支持 TypeScriptdefineStore
+ ref
/reactive
)reactive()
包裹 statecomputed
)在 Action 中统一捕获,try,catch进行错误捕获
在路由守卫中判断用户是否登录,或者工具函数中进行状态处理