目录
pinia的使用(三):$patch---$reset---$subscribe
pinia的安装
yarn add pinia / npm install pinia
在main.js中引入
import { createApp } from "vue";
import App from "./App.vue";
import {createPinia} from 'pinia'
const pinia = createPinia()
createApp(App).use(pinia).mount("#app")
pinia的创建
import { defineStore } from "pinia";
import { ref } from 'vue'
// defineStore 的第一个参数为唯一ID 第二个参数可以是一个函数 也可以是一个对象
// 此方法类似于vue的vuex
export const useTextStore = defineStore("testStore", {
// state 作为仓库的data
state: () => ({
counter: 12,
}),
// getters 作为商店的computed 属性
getters: {
double: (state) => state.counter * 2,
},
// actions 作为商店的methods 属性
actions: {},
});
// 此方法类似于vue3的setup
export const useText1Store = defineStore("testStore1", () => {
const counter = ref(50)
const number = ref(100)
// 调用其他store中的数据
const Ustore = useTextStore()
const doubleCounter = computed(() => counter.value * 2 + Ustore.counter);
function getCounter() {
counter.value++;
}
function getNumber() {
number.value++;
}
return {
counter,
doubleCounter,
getCounter,
number,
getNumber,
};
});
pinia的使用(一):
<template>
{{ store.counter }}
{{ store.num }}
{{ counter }}
</template>
<script>
import { storeToRefs } from 'pinia'
import { useTextStore, useText1Store } from '../store/index'
export default {
setup() {
const store = useTextStore()
// store.counter -- 0
// store 是一个用 reactive 包裹的对象,这意味着不需要在 getter 之后写 .value 但是,就像 setup 中的 props 一样,我们不能解构它
const store1 = useText1Store()
// 无法解构,会破坏反应性
// const { counter, doubleCounter } = store1
// 当想使用解构的方法拿到store并具有响应式 要使用 storeToRefs
const { counter, doubleCounter } = storeToRefs(store1)
const { getCounter } = store1
setTimeout(() => {
store.counter++
store.num = 2
},1000)
getCounter() // 在未使用storeToRefs依然是1 ,使用后变成响应式
setTimeout(() => {
store.$reset() // 使store里的值变成初始值
},2000)
return {
store,
counter, // 不会发生变化
doubleCounter // 不会发生变化
}
}
}
</script>
<template>
{{ number }}-- {{ counter }}-- {{ doubleCounter }}
<div style="width:30px;height: 30px;background: red" @click="fn1"></div>
</template>
<script setup>
import { storeToRefs } from 'pinia'
import { useTextStore, useText1Store } from '../store/index'
const store1 = useText1Store()
const { counter, doubleCounter, number } = storeToRefs(store1)
const { getCounter, getNumber } = store1
getCounter()
const fn1 = () => {
getNumber()
}
</script>
pinia的使用(二):
Setup store 也可以依赖于全局提供的属性,比如路由。任何应用层面提供的属性都可以在 store 中使用 inject()
// APP.vue
import { ref, provide } from 'vue'
const hreoName = ref('失败的man')
provide('hreoName', hreoName)
// test2.js
import { defineStore } from "pinia";
import { useRoute } from 'vue-router'
import { inject } from "vue";
export const useProjectStore = defineStore("testStore1", () => {
const route = useRoute();
const name = inject("hreoName");
return {
route,
name
};
});
// test2.vue
<template>
{{ store.name }}
</template>
<script setup>
import { computed } from 'vue'
import { useProjectStore } from '../store/index'
const store = useProjectStore()
</script>
pinia的使用(三):$patch---$reset---$subscribe
$patch:方法是用于局部更新状态的方法。它允许您通过传递一个包含要更新的属性的对象来更新状态。可用于批量修改数据。
$reset:
1. $reset是pinia中的静态方法,它用于清空当前的响应式状态,当我们需要在组件中重置状态的时候就可以使用$reset
2. 参数: 1.target 可选 定义要重置目标状态对象 2. options 可选,定义重置选项,如回调函数过渡动画
3. 其参数有以下用途:
①. 重置特定状态:当我们需要在组件中根据不同场景切换状态时,可以使用 $reset 方法清空当前状态,以便重新设置新的状态。
②.初始化状态:在组件初始化时,可以使用 $reset 方法将状态重置为初始值,以便进行后续操作。
③.清除副作用:当组件中的状态受到副作用影响时,可以使用 $reset 方法清除副作用,使状态恢复到正常状态。
$subscribe:
你可以通过 store 的 $subscribe()
方法侦听 state 及其变化。比起普通的 watch()
,使用 $subscribe()
的好处是 subscriptions 在 patch 后只触发一次
// vue3中setup pinia无法使用$reset 需要在main.js重写
pinia.use(({ store }) => {
const initialState = JSON.parse(JSON.stringify(store.$state));
store.$reset = () => {
store.$patch(initialState);
};
});
// 使用
<template>
{{ store1.number }}-- {{ store1.counter }}-- {{ store1.doubleCounter }}
<div style="height: 30px;border: 1px solid #000" @click="fn1">点击变更状态</div>
<div style="height: 30px;border: 1px solid #000" @click="fn2">点击还原状态</div>
</template>
<script setup>
import { storeToRefs } from 'pinia'
import { useTextStore, useText1Store } from '../store/index'
const store1 = useText1Store()
const { getCounter, getNumber } = store1
getCounter()
const fn1 = () => {
store1.$patch({
number: 10,
counter: store1.counter++,
})
}
const fn2 = () => {
store1.$reset()
}
</script>
<template>
{{ store1.number }}-- {{ store1.counter }}-- {{ store1.doubleCounter }}
<div style="height: 30px;border: 1px solid #000" @click="fn1">点击变更状态</div>
</template>
<script setup>
import { storeToRefs } from 'pinia'
import { useTextStore, useText1Store } from '../store/index'
const store1 = useText1Store()
const { getCounter, getNumber } = store1
getCounter()
const fn1 = () => {
store1.$patch({
number: 10,
counter: store1.counter++,
})
}
// 监听 $patch
store1.$subscribe((mutation, state) => {
console.log(mutation, state);
// 每当状态发生变化时,将整个 state 持久化到本地存储。
localStorage.setItem('cart', JSON.stringify(state))
},{ detached: true })
// 默认情况下,state subscription 会被绑定到添加它们的组件上 (如果 store 在组件的 setup() 里面)。这意味着,当该组件被卸载时,它们将被自动删除。如果你想在组件卸载后依旧保留它们,请将 { detached: true } 作为第二个参数,以将 state subscription 从当前组件中分离:
</script>
pinia的使用(四):$onAction
通过 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}.`
)
})
},
true
// 默认情况下,action 订阅器会被绑定到添加它们的组件上(如果 store 在组件的 setup() 内)。这意味着,当该组件被卸载时,它们将被自动删除。如果你想在组件卸载后依旧保留它们,请将 true 作为第二个参数传递给 action 订阅器,以便将其从当前组件中分离
)
// 手动删除监听器
unsubscribe()
插件
Pinia store 现在可支持扩展:
1. 为 store 添加新的属性
2. 定义 store 时增加新的选项
3. 为 store 增加新的方法
4. 包装现有的方法
5. 改变甚至取消 action
6. 实现副作用,如本地存储
7. 仅应用插件于特定 store
Pinia插件是一个函数,可以选择性地返回要添加到 store 的属性。它接收一个可选参数,即 context。
export function myPiniaPlugin(context) {
context.pinia // 用 `createPinia()` 创建的 pinia。
context.app // 用 `createApp()` 创建的当前应用(仅 Vue 3)。
context.store // 该插件想扩展的 store
context.options // 定义传给 `defineStore()` 的 store 的可选对象。
// ...
}
一、拓展Store
可以直接通过在一个插件中返回包含特定属性的对象来为每个 store 都添加上特定属性:
pinia.use(() => ({ hello: 'world' }))
也可以直接在 store
上设置该属性,如果想被devtools 自动追踪请使用返回对象的方法
pinia.use(({ store }) => {
store.hello = 'world'
})
任何由插件返回的属性都会被 devtools 自动追踪,所以如果你想在 devtools 中调试 hello
属性,为了使 devtools 能追踪到 hello
,请确保在 dev 模式下将其添加到store._customProperties
中
pinia.use(({ store }) => {
store.hello = 'world'
// 确保你的构建工具能处理这个问题,webpack 和 vite 在默认情况下应该能处理。
if (process.env.NODE_ENV === 'development') {
// 添加你在 store 中设置的键值
store._customProperties.add('hello')
}
})
每个 store 都被 reactive包装过,所以可以自动解包任何它所包含的ref,所以在下列示例中不需要.value 去获取变量的数值
const sharedRef = ref('shared')
pinia.use(({ store }) => {
// 每个 store 都有单独的 `hello` 属性
store.hello = ref('secret')
// 它会被自动解包
store.hello // 'secret'
// 所有的 store 都在共享 `shared` 属性的值
store.shared = sharedRef
store.shared // 'shared'
})
二、添加新的 state
给 store 添加新的 state 属性或者在服务端渲染的激活过程中使用的属性,你必须同时在两个地方添加它,并且使用响应式的API让其能够在多个组件中共享相同的数据。
①:在 store
上,然后你才可以用 store.myState
访问它
②:在 store.$state
上,然后你才可以在 devtools 中使用它,并且,在 SSR 时被正确序列化
import { toRef, ref } from 'vue'
pinia.use(({ store }) => {
if (!Object.prototype.hasOwnProperty(store.$state, 'hasError')) {
// 在插件中定义 hasError,因此每个 store 都有各自的
// hasError 状态
const hasError = ref(false)
store.$state.hasError = hasError
}
store.hasError = toRef(store.$state, 'hasError')
// 在这种情况下,最好不要返回 `hasError`
// 因为它将被显示在 devtools 的 `state` 部分
// 如果我们返回它,devtools 将显示两次。
})
三、添加新的外部属性
当添加外部属性、第三方库的类实例或非响应式的简单值时,你应该先用 markRaw()
来包装一下它,再将它传给 pinia
import { markRaw } from 'vue'
import { router } from './router'
pinia.use(({ store }) => {
store.router = markRaw(router)
})
四、在插件中调用 $subscribe
pinia.use(({ store }) => {
store.$subscribe(() => {
// 响应 store 变化
})
store.$onAction(() => {
// 响应 store actions
})
})
五、添加新的选项
在定义 store 时,可以创建新的选项,以便在插件中使用它们。例如,你可以创建一个 debounce
选项,允许你让任何 action 实现防抖
defineStore('search', {
actions: {
searchContacts() {
// ...
},
},
// 这将在后面被一个插件读取
debounce: {
// 让 action searchContacts 防抖 300ms
searchContacts: 300,
},
})
该插件可以读取该选项来包装 action,并替换原始 action:
// 使用任意防抖库
import debounce from 'lodash/debounce'
pinia.use(({ options, store }) => {
if (options.debounce) {
// 我们正在用新的 action 来覆盖这些 action
return Object.keys(options.debounce).reduce((debouncedActions, action) => {
debouncedActions[action] = debounce(
store[action],
options.debounce[action]
)
return debouncedActions
}, {})
}
})
注意,在使用 setup 语法时,自定义选项作为第 3 个参数传递:
defineStore(
'search',
() => {
// ...
},
{
// 这将在后面被一个插件读取
debounce: {
// 让 action searchContacts 防抖 300ms
searchContacts: 300,
},
}
)