irpas技术客

Vue3——第十四章(Pinia:值得你喜欢的 Vue Store)_whyfail

未知 2744

一、Pinia 简介 Pinia 是 Vue 的专属状态管理库,它允许你跨组件或页面共享状态。Pinia 起源于一次探索 Vuex 下一个迭代的实验,因此结合了 Vuex 5 核心团队讨论中的许多想法。mutation 已被弃用。它们经常被认为是极其冗余的。它们初衷是带来 devtools 的集成方案,但这已不再是一个问题了。Vuex 3.x 只适配 Vue 2,而 Vuex 4.x 是适配 Vue 3 的。极致轻量化:Pinia 大小只有 1kb 左右开发工具支持:不管是 Vue 2 还是 Vue 3,支持 Vue devtools 钩子的 Pinia 都能给你更好的开发体验。类型安全:类型可自动推断,即使在 JavaScript 中亦可为你提供自动补全功能!Pinia 起始于 2019 年 11 月左右的一次实验,其目的是设计一个拥有组合式 API 的 Vue 状态管理库。从那时起,就倾向于同时支持 Vue 2 和 Vue 3,并且不强制要求开发者使用组合式 API。 二、基础示例 下面就是 pinia API 的基本用法你可以先创建一个 Store: // stores/counter.js import { defineStore } from 'pinia' export const useCounterStore = defineStore('counter', { state: () => { return { count: 0 } }, // 也可以这样定义 // state: () => ({ count: 0 }) actions: { increment() { this.count++ }, }, }) 为实现更多高级用法,你甚至可以使用一个函数(与组件 setup() 类似)来定义一个 Store: export const useCounterStore = defineStore('counter', () => { const count = ref(0) function increment() { count.value++ } return { count, increment } }) 然后你就可以在一个组件中使用该 store 了: import { useCounterStore } from '@/stores/counter' export default { setup() { const counter = useCounterStore() counter.count++ // 带有自动补全 ? counter.$patch({ count: counter.count + 1 }) // 或者使用 action 代替 counter.increment() }, } 如果你还不熟悉 setup() 函数和组合式 API,Pinia 也提供了一组类似 Vuex 的 映射 state 的辅助函数,通过 mapStores()、mapState() 或 mapActions() 访问: export default { computed: { // 其他计算属性 // ... // 允许访问 this.counterStore 和 this.userStore ...mapStores(useCounterStore, useUserStore) // 允许读取 this.count 和 this.double ...mapState(useCounterStore, ['count', 'double']), }, methods: { // 允许读取 this.increment() ...mapActions(useCounterStore, ['increment']), }, } 在非组件的 js 文件中使用: import { useCounterStore } from '@/stores/counter' function xxxx = () =>{ const counter = useCounterStore() counter.count++ counter.increment() } 三、核心概念 1、定义 Store 我们得知道 Store 是用 defineStore() 定义的,它的第一个参数要求是一个独一无二的名字: import { defineStore } from 'pinia' // 你可以对 `defineStore()` 的返回值进行任意命名 // 但最好使用 store 的名字,同时以 `use` 开头且以 `Store` 结尾。(比如 `useUserStore`,`useCartStore`,`useProductStore`) // 第一个参数是你的应用中 Store 的唯一 ID。 export const useStore = defineStore('main', { // 其他配置... }) 这个名字 ,也被用作 id ,是必须传入的, Pinia 将用它来连接 store 和 devtools。defineStore() 的第二个参数可接受两类值:Setup 函数或 Option 对象。与 Vue 的选项式 API 类似,我们也可以传入一个带有 state、actions 与 getters 属性的 Option 对象: // 可以认为 state 是 store 的数据 (data) //getters 是 store 的计算属性 (computed) //而 actions 则是方法 (methods) export const useCounterStore = defineStore('counter', { state: () => ({ count: 0 }), getters: { double: (state) => state.count * 2, }, actions: { increment() { this.count++ }, }, }) 与 Vue 组合式 API 的 setup 函数 相似,我们可以传入一个函数,该函数定义了一些响应式属性和方法,并且返回一个带有我们想暴露出去的属性和方法的对象。 export const useCounterStore = defineStore('counter', () => { const count = ref(0) function increment() { count.value++ } return { count, increment } }) 在 Setup Store 中:ref() 就是 state 属性、computed() 就是 getters、function() 就是 actionsSetup store 比 Option Store 带来了更多的灵活性,因为你可以在一个 store 内创建侦听器,并自由地使用任何组合式函数。 2、State 在大多数情况下,state 都是你的 store 的核心。人们通常会先定义能代表他们 APP 的 state。在 Pinia 中,state 被定义为一个返回初始状态的函数。这使得 Pinia 可以同时支持服务端和客户端。 import { defineStore } from 'pinia' const useStore = defineStore('storeId', { // 为了完整类型推理,推荐使用箭头函数 state: () => { return { // 所有这些属性都将自动推断出它们的类型 count: 0, name: 'Eduardo', isAdmin: true, items: [], hasChanged: true, } }, }) 默认情况下,你可以通过 store 实例访问 state,直接对其进行读写。 const store = useStore() store.count++ 可以通过调用 store 的 $reset() 方法将 state 重置为初始值。 const store = useStore() store.$reset() 除了用 store.count++ 直接改变 store,你还可以调用 $patch 方法。它允许你用一个 state 的补丁对象在同一时间更改多个属性: store.$patch({ count: store.count + 1, age: 120, name: 'DIO', }) $patch 方法也接受一个函数来组合这种难以用补丁对象实现的变更。 store.$patch((state) => { state.items.push({ name: 'shoes', quantity: 1 }) state.hasChanged = true }) 你不能完全替换掉 store 的 state,因为那样会破坏其响应性。但是,你可以 patch 它。 // 这实际上并没有替换`$state` store.$state = { count: 24 } // 在它内部调用 `$patch()`: store.$patch({ count: 24 }) 3、Getter Getter 完全等同于 store 的 state 的计算值。可以通过 defineStore() 中的 getters 属性来定义它们。推荐使用箭头函数,并且它将接收 state 作为第一个参数: export const useStore = defineStore('main', { state: () => ({ count: 0, }), getters: { doubleCount: (state) => state.count * 2, }, }) 大多数时候,getter 仅依赖 state,不过,有时它们也可能会使用其他 getter。因此,即使在使用常规函数定义 getter 时,我们也可以通过 this 访问到整个 store 实例 export const useStore = defineStore('main', { state: () => ({ count: 0, }), getters: { // 自动推断出返回类型是一个 number doubleCount(state) { return state.count * 2 }, // 返回类型**必须**明确设置 doublePlusOne(): number { // 整个 store 的 自动补全和类型标注 ? return this.doubleCount + 1 }, }, }) 然后你可以直接访问 store 实例上的 getter 了: <template> <p>Double count is {{ store.doubleCount }}</p> </template> <script> export default { setup() { const store = useStore() return { store } }, } </script> 4、Action Action 相当于组件中的 method。它们可以通过 defineStore() 中的 actions 属性来定义,并且它们也是定义业务逻辑的完美选择。 export const useStore = defineStore('main', { state: () => ({ count: 0, }), actions: { increment() { this.count++ }, randomizeCounter() { this.count = Math.round(100 * Math.random()) }, }, }) 类似 getter,action 也可通过 this 访问整个 store 实例,并支持完整的类型标注(以及自动补全?)。不同的是,action 可以是异步的,你可以在它们里面 await 调用任何 API,以及其他 action! 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 } }, }, }) Action 可以像方法一样被调用: export default defineComponent({ setup() { const main = useMainStore() // 作为 store 的一个方法调用该 action main.randomizeCounter() return {} }, }) 想要使用另一个 store 的话,那你直接在 action 中调用就好了: 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') } }, }, })


1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,会注明原创字样,如未注明都非原创,如有侵权请联系删除!;3.作者投稿可能会经我们编辑修改或补充;4.本站不提供任何储存功能只提供收集或者投稿人的网盘链接。

标签: #Vue #Store