一、快速了解pinia
Pinia 是 Vue 的存储库,它允许您跨组件/页面共享状态,其实就是vuex5
显著特点:
- 支持 options api 和 composition api 两种语法创建 Store
- 不再使用mutations
- 没有嵌套模块,而是用组合的 stores 来代替
- 对 ts 的支持
- 体积小,仅1kb
- 支持 Vue devtools、SSR 和 webpack 代码拆分
这里是 Pinia 官网提供的一个在线的 Pinia Demo,大家可以在上面学习。
pinia示意图:
vuex示意图
二、基本使用
安装
yarn add pinia
# 或者使用 npm
npm install pinia
如果您的应用使用 Vue 2,您还需要安装组合 API:
@vue/composition-api。
vue3中创建一个 pinia(根存储)并将其传递给应用程序:
import { createPinia } from 'pinia'
app.use(createPinia())
vue2的话还需要安装一个插件并将创建的 pinia 注入应用程序的根目录
import { createPinia, PiniaVuePlugin } from 'pinia'
Vue.use(PiniaVuePlugin)
const pinia = createPinia()
new Vue({
el: '#app',
// 其他选项...
// ...
// 注意同一个 `pinia` 实例可以在多个 Vue 应用程序中使用
// 同一个页面
pinia,
})
在 Vue 2 中,Pinia 使用 Vuex 的现有接口(因此不能与vuex一起使用)。
Store
定义一个store:使用defineStore()定义的,第一个参数是应用程序中store的唯一id
import { defineStore } from 'pinia'
export const useStore = defineStore('demo',{
// other options
})
使用store
import { storeToRefs } from 'pinia'
export default defineComponent({
setup() {
const store = useStore()
// ❌ 这不起作用,因为它会破坏响应式
const { name } = store
// 是响应式的
const { age } = storeTorefs(store)
return {
// 一直会是 "eduardo"
name,
// 这将是响应式的
doubleValue: computed(() => store.doubleCount),
age,
}
},
})
Pinia 不支持嵌套存储。相反,它允许你根据需要创建store。但是,store仍然可以通过在另一个store中导入和使用store来隐式嵌套
State
定义
import { defineStore } from 'pinia'
const useStore = defineStore('demo',{
state:()=>({
counter:0
})
})
修改state
- 直接对state中的值进行修改
const store = useStore()
store.counter++
- $patch方法,使用使用部分state对象同时应用多个修改,也可以传入一个函数,批量进行处理
store. $patch({
counter: store.counter + 1,
name: 'Abalam',
})
store.$patch(state=>{
state.items.push({ name: 'shoes', quantity: 1 })
state.hasChanged = true
})
- 替换整个state
store.$state = { counter: 666, name: 'Paimon'}
- 重置状态
store.$reset()
-
订阅状态
可以通过 store 的 $subscribe() 方法查看状态及其变化,类似于 Vuex 的 subscribe方法。 与常规的 watch() 相比,使用 $subscribe() 的优点是subscriptions只会在patches之后触发一次。默认情况下,state subscriptions 绑定到添加它们的组件(如果 store 位于组件的 setup() 中)。 意思是,当组件被卸载时,它们将被自动删除。 如果要在卸载组件后保留它们,请将 { detached: true } 作为第二个参数传递给detach当前组件的state subscription
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 // 补丁对象传递给 to cartStore.$patch() // 每当它发生变化时,将整个状态持久化到本地存储 localStorage.setItem('cart', JSON.stringify(state)) },{ // 此订阅将在组件卸载后保留 detached: true })
Getters
定义:可以类比计算属性,也可以通过 this 来直接访问store中的其他 gettters。 同样,getters 也具有缓存特性,同一个 getters 就算被调用多次,只要值不变,依旧只执行一次
import { defineStore } from 'pinia'
const useStore = defineStore('demo',{
state:() => ({
counter: 0
})
getters:{
doubleCount: state => state.counter * 2,
doubleNum(): number {
return this.counter * 2 + 1
}
// 当接收参数时,此时不具有缓存特性
getUserById: (state) => {
return (userId) => state.users.find((user) => user.id === userId)
},
}
})
访问其他getters:与计算属性一样,可以组合多个 getters,通过this.去访问其他getters。
export default defineStore('common', {
getters: {
// 通过state具体看代码
doubleCount: (state) => state.count * 2,
// 必须显示定义返回的类型,内部通过this访问store中的数据
doubleCountPlus(): number {
// autocompletion and typings for the whole store ✨
return this .doubleCount * 2 + 1
},
otherGetter(state) {
// 访问其他store的getters
const otherStore = useOtherStore()
return state.localData + otherStore.data
},
}
});
Actions
pinia中没有mutations。Store中的Actions 配置项可以执行同步或异步方法,且 action 被调用的是为常规的函数调用,而不是使用 dispatch 方法或 MapAction 辅助函数
定义
import { defineStore } from 'pinia'
const useStore = defineStore('demo',{
state:() => ({
counter: 0
})
actions:{
increment() {
this.counter++
},
}
})
actions 可以是异步的,您可以在其中await 任何 API 调用甚至其他操作!
import { defineStore } from 'pinia'
const useStore = defineStore('demo',{
state:() => ({
counter: 0
})
actions:{
async registerUser(login, password) {
try {
this.userData = await api.post({ login, password })
} catch (error) {
showTooltip(error)
// 让表单组件显示错误
return error
}
},
// 使用另一个store
async fetchUserPreferences(preferences) {
const auth = useAuthStore ()
if (auth.isAuthenticated) {
this.preferences = await fetchPreferences()
} else {
throw new Error('User must be authenticated')
}
},
}
})
action 间的相互调用,直接用 this 访问即可。
订阅actions
-
可以使用 store.$onAction() 订阅 action 及其结果。 传递给它的回调在 action 之前执行。after 处理 Promise 并允许您在 action 完成后执行函数。默认情况下,action subscriptions绑定到添加它们的组件(如果 store 位于组件的 setup() 内)。意思是,当组件被卸载时,它们将被自动删除。 如果要在卸载组件后保留它们,请将 true 作为第二个参数传递给当前组件的detach action subscription:
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()
Plugins
定义:
1. Pinia 插件是一个函数,可以选择返回要添加到 store 的属性。 它需要一个可选参数,一个 *context*:
1. ```
export function myPiniaPlugin(context) {
context.pinia // 使用 `createPinia()` 创建的 pinia
context.app // 使用 `createApp()` 创建的当前应用程序(仅限 Vue 3)
context.store // 插件正在扩充的 store
context.options // 定义存储的选项对象传递给`defineStore()`
// ...
}
```
- 基本使用
- ```
import { createPinia } from 'pinia'
// 为安装此插件后创建的每个store添加一个名为 `secret` 的属性
// 这可能在不同的文件中
function SecretPiniaPlugin() {
return { secret: 'the cake is a lie' }
}
const pinia = createPinia()
// 将插件提供给 pinia
pinia.use(SecretPiniaPlugin)
// 在另一个文件中
const store = useStore()
store.secret // 'the cake is a lie'
```
- 通过一些底层API,可以对pinia store进行扩展:
1. 向 Store 添加新属性
1. 定义 Store 时添加新选项
1. 为 Store 添加新方法
1. 包装现有方法
1. 更改甚至取消操作
1. 实现本地存储等副作用
1. 仅适用于特定 Store
数据持久化
插件 pinia-plugin-persist 可以辅助实现数据持久化功能。
- 安装
npm i pinia-plugin-persist - 使用
import { createPinia } from 'pinia' import piniaPluginPersist from 'pinia-plugin-persist' const store = createPinia() store.use(piniaPluginPersist) export default store - 在对应的store开启persist即可
export const useUserStore = defineStore('user', {
// 开启数据缓存,数据默认存在 sessionStorage 里,并且会以 store 的 id 作为 key。
persist: {
enabled: true
},
state: () => {
return {
name: 'yunmu'
}
}
})
- 自定义key值
persist: {
enabled: true,
strategies: [
{
key: 'userInfo',
// 从sessionStorage转为localStorage
storage: localStorage,
}
]
}
- 默认所有 state 都会进行缓存,可以通过 paths 指定要持久化的字段,其他的则不会进行持久化。
state: () => {
return {
name: 'yunmu',
age: 18,
gender: '男'
}
},
// 只持久存储name和age到localStorage
persist: {
enabled: true,
strategies: [
{
storage: localStorage,
paths: ['name', 'age']
}
]
}