🍍🍍🍍
介绍(摘自官方文档)
Pinia 是 Vue 的存储库,它允许您跨组件/页面共享状态。 如果您熟悉 Composition API,您可能会认为您已经可以通过一个简单的 export const state = reactive({}). 这对于单页应用程序来说是正确的,但如果它是服务器端呈现的,会使您的应用程序暴露于安全漏洞。 但即使在小型单页应用程序中,您也可以从使用 Pinia 中获得很多好处:
与 Vuex 的比较(摘自官方文档)
Pinia 最初是为了探索 Vuex 的下一次迭代会是什么样子,结合了 Vuex 5 核心团队讨论中的许多想法。最终,我们意识到 Pinia 已经实现了我们在 Vuex 5 中想要的大部分内容,并决定实现它 取而代之的是新的建议。
与 Vuex 相比,Pinia 提供了一个更简单的 API,具有更少的规范,提供了 Composition-API 风格的 API,最重要的是,在与 TypeScript 一起使用时具有可靠的类型推断支持。
开始
安装
1 2 3
| yarn add pinia
npm install pinia
|
创建一个 pinia(根存储)并将其传递给应用程序
1 2
| import { createPinia } from 'pinia' app.use(createPinia())
|
什么时候应该使用 Store
存储应该包含可以在整个应用程序中访问的数据。这包括在许多地方使用的数据,例如导航栏中显示的用户信息,以及需要通过页面保留的数据,例如一个非常复杂的多步骤表格。
另一方面,您应该避免在存储中包含可以托管在组件中的本地数据,例如页面本地元素的可见性。
并非所有应用程序都需要访问全局状态,但如果您需要一个,Pania 将使您的生活更轻松。
定义一个 Store
在项目的src目录创建一个名为store的文件夹,内部创建ts文件名为index.ts
1 2 3 4 5 6 7 8
| import { defineStore } from 'pinia'
export const usePinia = defineStore("piniaDemo", { })
|
组件内使用store
1 2 3 4 5 6
| <script setup lang="ts"> import { usePinia } from '@/store/index'; const store = usePinia(); </script>
|
如需解构store内属性
需要引入storeToRefs包裹store再进行解构属性,为响应式
1 2 3 4 5 6
| const { name } = store;
import { storeToRefs } from 'pinia'; const { name } = storeToRefs(store);
|
State
定义state初始属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import { defineStore } from 'pinia' export const usePinia = defineStore("piniaDemo", { state: () => { return { counter: 0, name: '天天', isAdmin: true, } }, })
|
访问State属性&修改属性值
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <script setup lang="ts"> import { usePinia } from '@/store/index'; const store = usePinia(); const changeName = () => { store.name = '真白'; }; </script> <template> <div>{{ store.name }}</div> <button @click="changeName">修改name</button> </template>
|
重置State状态
您可以通过调用 store 上的 $reset() 方法将状态 重置 到其初始值:
1 2
| const store = useStore() store.$reset()
|
改变状态
除了直接用 store.counter++ 修改 store,你还可以调用 $patch 方法。 它允许您使用部分“state”对象同时应用多个更改:
1 2 3 4 5 6 7
| const changeStore = () => { store.$patch({ counter: store.counter + 1, name: '天天', isAdmin: !store.isAdmin, }); };
|
$patch方法除了接收对象以外,还可以接收一个函数作为参数
1 2 3 4 5 6
| const changeStore = () => { store.$patch((state) => { st.name = '天天'; }); };
|
替换state
可以通过将其 $state 属性设置为新对象来替换 Store 的整个状态:
1 2 3 4 5 6 7
| const changeStore = () => { store.$state = { counter: 100, name: 'xxx', isAdmin: false, }; };
|
订阅状态
每当state被修改后,$subscribe()方法就会被调用
1 2 3 4 5 6
| store.$subscribe((mutation, state) => { console.log('🚀 ~ file: App.vue:15 ~ store.$subscribe ~ state', state); console.log('🚀 ~ file: App.vue:15 ~ store.$subscribe ~ mutation', mutation); localStorage.setItem('pinia', JSON.stringify(state)); });
|
Getters
Getter 完全等同于 Store 状态的计算值。 它们可以用 defineStore() 中的 getters 属性定义。 他们接收“状态”作为第一个参数
定义getters
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| export const usePinia = defineStore("piniaDemo", { state: () => { return { counter: 999, name: '真白', isAdmin: true, } }, getters: { getName(state) { return state.name }, doubleCount(): number { return this.counter * 2 }, } })
|
访问getters
可以通过store实例 直接访问getters
1 2 3
| <template> <div>{{ store.getName }}---{{ store.doubleCount }}</div> </template>
|
访问其他getters
与计算属性一样,可以组合多个 getter。 通过 this 访问任何其他 getter。 即使您不使用 TypeScript,您也可以使用 JSDoc 提示您的 IDE 类型
getters接收参数
这里需要用到函数柯里化的内容,通过返回函数的形式来接收任何参数
1 2 3 4 5
| getters: { doubleCount(state) { return (num: number) => state.counter + num } }
|
组件中使用
1
| <div>{{ store.doubleCount(99) }}</div>
|
注意:请注意,在执行此操作时,getter 不再缓存,它们只是您调用的函数。 但是,您可以在 getter 本身内部缓存一些结果。
访问其他 Store 的getter
1 2 3 4 5 6 7 8 9 10 11
| import { useOtherStore } from './otherStore' export const usePinia = defineStore("piniaDemo", { getters: { doubleCount(state) { const otherStore = useOtherStore() return `${otherStore.changeName}---${state.name}` } } })
|
Actions
定义actions
Actions 相当于组件中的 methods。 它们可以使用 defineStore() 中的 actions 属性定义,并且它们非常适合定义业务逻辑:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import { defineStore } from 'pinia' export const usePinia = defineStore("piniaDemo", { state: () => { return { counter: 999, } }, actions: { increment() { this.counter++ }, } })
|
actions 可以是异步的,您可以在其中await 任何 API 调用甚至其他操作
模拟一下async await的使用,简单定义个返回promise的函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| import { defineStore } from 'pinia'
const proDemo = () => { return new Promise<string>((resolve, reject) => { setTimeout(() => { resolve("到时间了") }, 2000); }) } export const usePinia = defineStore("piniaDemo", { state: () => { return { counter: 999, } }, actions: { async increment() { try { let data = await proDemo() console.log("🚀 ~ file: index.ts:22 ~ increment ~ data", data) } catch (error) { console.log("报错了", error); } }, } })
|
在组件中调用
1 2 3 4 5
| <script setup lang="ts"> import { usePinia } from '@/store/index'; const store = usePinia(); store.increment(); </script>
|
访问其他 store 操作
要使用另一个 store ,可以直接在操作内部使用它
1 2 3 4 5 6 7 8 9 10 11
| import { useOtherStore } from './otherStore' export const usePinia = defineStore("piniaDemo", { actions: { increment() { const otherStore = useOtherStore() let name = otherStore.getName() console.log("🚀 ~ file: index.ts:22 ~ increment ~ name", name) }, } })
|
订阅 Actions
可以使用 store.$onAction() 订阅 action 及其结果。 传递给它的回调在 action 之前执行。 after 处理 Promise 并允许您在 action 完成后执行函数。 以类似的方式,onError 允许您在处理中抛出错误。 这些对于在运行时跟踪错误很有用,类似于 Vue 文档中的这个提示。
这是一个在运行 action 之前和它们 resolve/reject 之后记录的示例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| const unsubscribe = someStore.$onAction( ({ name, // action 的名字 store, // store 实例 args, // 调用这个 action 的参数 after, // 在这个 action 执行完毕之后,执行这个函数 onError, // 在这个 action 抛出异常的时候,执行这个函数 }) => { const startTime = Date.now() console.log(`Start "${name}" with params [${args.join(', ')}].`) after((result) => { console.log( `Finished "${name}" after ${ Date.now() - startTime }ms.\nResult: ${result}.` ) }) onError((error) => { console.warn( `Failed "${name}" after ${Date.now() - startTime}ms.\nError: ${error}.` ) }) } )
unsubscribe()
|
默认情况下,action subscriptions 绑定到添加它们的组件(如果 store 位于组件的 setup() 内)。 意思是,当组件被卸载时,它们将被自动删除。 如果要在卸载组件后保留它们,请将 true 作为第二个参数传递给当前组件的 detach action subscription:
1 2 3 4 5 6 7 8 9 10
| export default { setup() { const someStore = useSomeStore() someStore.$onAction(callback, true) }, }
|