0. 前言
Pinia 是测试 Vuex 下一次迭代的提案
Vuex5的提案,相比 vuex3和4有很大的改进
- 支持
options api
andcomposition api
- 没有
mutations
- 没有嵌套的模块
- 更好
typescript
支持 - 自动化的代码拆分
1. 与Vuex的比较
Pinia 试图尽可能接近 Vuex 的理念。它旨在测试 Vuex 下一次迭代的提案,并且取得了成功,目前有一个针对 Vuex 5 的开放 RFC,其 API 与Pinia 使用的 API 非常相似。请注意,Pinia 的作者 I (Eduardo) 是 Vue.js 核心团队的一员,并积极参与 Router 和 Vuex 等 API 的设计。对这个项目的意图是重新设计使用全局 Store 的体验,同时保持 Vue 平易近人的哲学。让 Pania 的 API 与 Vuex 一样接近,因为它不断向前发展,可以轻松迁移到 Vuex,甚至在未来融合这两个项目。
Pinia API 与 Vuex ≤4 有很大不同,:
- 没有
mutations
。mutations被认为是非常冗长的。最初带来了 devtools 集成,但这不再是问题。 - 不再有模块的嵌套结构。您仍然可以通过在另一个store中导入和使用store来隐式嵌套store,但 Pinia 通过设计提供扁平结构,同时仍然支持store之间的交叉组合方式。您甚至可以拥有store的循环依赖关系。
- 更好
typescript
支持。无需创建自定义的复杂包装器来支持 TypeScript,所有内容都是类型化的,并且 API 的设计方式尽可能地利用 TS 类型推断。 - 不再需要注入、导入函数、调用它们,享受自动补全!
- 无需动态添加stores,默认情况下它们都是动态的,您甚至不会注意到。请注意,您仍然可以随时手动使用store来注册它,但因为它是自动的,所以您无需担心。
- 没有命名空间模块。鉴于store的扁平架构,“命名空间”store是其定义方式所固有的,您可以说所有stores都是命名空间的。
2. Pinia优点
2.1 直观
Stores 和组件一样熟悉。API 旨在让您编写组织良好的stores。
2.2 类型安全
类型是推断出来的,这意味着即使在 JavaScript 中,stores也为您提供自动完成功能
2.3 开发者工具支持
Pinia 与 Vue devtools 挂钩,提供增强的 Vue 2 和 Vue 3 开发体验。
2.4 可扩展
响应存储更改以通过事务、本地存储同步等扩展 Pinia。
2.5 模块化设计
构建多个stores并让打包程序代码自动拆分它们。
2.6 极轻
Pinia 大小约1kb
3.基本使用
// stores/counter.js
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => {
return { count: 0 }
},
// could also be defined as
// state: () => ({ count: 0 })
actions: {
increment() {
this.count++
},
},
})
在组件中使用它:
import { useCounterStore } from '@/stores/counter'
export default {
setup() {
const counter = useCounterStore()
counter.count++
// with autocompletion ✨
counter.$patch({ count: counter.count + 1 })
// or using an action instead
counter.increment()
},
}
您甚至可以使用函数(类似于组件setup()
)为更高级的用例定义 Store:
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
function increment() {
count.value++
}
return { count, increment }
})
如果不熟悉setup()
Composition API,别担心,Pania 也支持类似 Vuex的 map helpers。您以相同的方式定义stores,使用mapStores()
, mapState()
, 或mapActions()
:
const useCounterStore = defineStore('counter', {
state: () => ({ count: 0 }),
getters: {
double: (state) => state.count * 2,
},
actions: {
increment() {
this.count++
}
}
})
const useUserStore = defineStore('user', {
// ...
})
export default {
computed: {
// other computed properties
// ...
// gives access to this.counterStore and this.userStore
...mapStores(useCounterStore, useUserStore)
// gives read access to this.count and this.double
...mapState(useCounterStore, ['count', 'double']),
},
methods: {
// gives access to this.increment()
...mapActions(useCounterStore, ['increment']),
},
}