大家好,我是老码农,今天来聊聊 Pinia,Vuex 的“后浪”
今天咱们不搞那些虚头巴脑的,直接开门见山,说说 Pinia 这个 Vue 3 生态里的状态管理新秀。不少同学可能还在 Vuex 的温柔乡里,但我得说,时代变了!Pinia 已经开始冲击 Vuex 的地位了。
那 Pinia 凭啥这么嚣张?它到底有哪些优势,设计哲学又是什么?别急,老码农我这就给你们扒个底朝天。
一、Vuex:老骥伏枥,志在千里,但有点老了
先简单回顾一下 Vuex,毕竟是老前辈,不能忘本。
Vuex 是 Vue 2 时代的官方状态管理库,它基于 Flux 架构,提供了一个集中式的数据存储,让组件可以共享状态,并且通过 mutations 提交更改,actions 异步操作,getters 获取数据。
优点嘛,稳定、成熟,社区庞大,资料丰富。
缺点嘛,也有,而且不少:
- 繁琐的 Boilerplate 代码: 每次定义一个 state,mutation,action,getter,都要写一堆样板代码,写多了人都麻了。尤其是命名空间,简直是噩梦。
- TypeScript 支持不友好: 虽然 Vuex 4 已经支持 TypeScript,但类型推断仍然不够智能,需要手动声明很多类型,烦!
- Mutation 的必要性: 强制使用 Mutation 来修改 state,虽然理论上保证了状态的可追踪性,但实际上增加了代码的复杂性,很多时候没必要。
- Composable API 兼容性: 在 Vue 3 的 Composition API 中,Vuex 的使用方式显得有些笨重,不够灵活。
举个例子,一个简单的计数器,用 Vuex 来实现:
// store/index.js
import { createStore } from 'vuex'
export default createStore({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
},
decrement (state) {
state.count--
}
},
actions: {
increment (context) {
context.commit('increment')
},
decrement (context) {
context.commit('decrement')
}
},
getters: {
doubleCount (state) {
return state.count * 2
}
}
})
// 组件中使用
import { useStore } from 'vuex'
import { computed } from 'vue'
export default {
setup() {
const store = useStore()
const count = computed(() => store.state.count)
const doubleCount = computed(() => store.getters.doubleCount)
const increment = () => {
store.dispatch('increment')
}
const decrement = () => {
store.dispatch('decrement')
}
return {
count,
doubleCount,
increment,
decrement
}
}
}
看着是不是有点眼花缭乱?
二、Pinia:轻装上阵,拥抱 Composition API
Pinia 就像一个年轻力壮的小伙子,浑身充满了活力,它吸取了 Vuex 的经验教训,并且紧跟 Vue 3 的潮流,彻底拥抱 Composition API。
Pinia 的核心设计理念可以用三个词概括:简单、直观、类型安全。
- 简单: 抛弃了 Mutations,直接用 Actions 修改 state,代码更简洁。
- 直观: API 设计更贴近 Vue 3 的 Composition API,更容易理解和使用。
- 类型安全: 完美支持 TypeScript,类型推断非常强大,告别手动声明类型的痛苦。
还是用上面的计数器例子,用 Pinia 来实现:
// store/counter.js
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0
}),
getters: {
doubleCount: (state) => state.count * 2
},
actions: {
increment() {
this.count++
},
decrement() {
this.count--
}
}
})
// 组件中使用
import { useCounterStore } from '@/store/counter'
import { computed } from 'vue'
export default {
setup() {
const counterStore = useCounterStore()
// 我们可以直接访问 state 和 actions
const count = computed(() => counterStore.count)
const doubleCount = computed(() => counterStore.doubleCount)
const increment = () => {
counterStore.increment()
}
const decrement = () => {
counterStore.decrement()
}
return {
count,
doubleCount,
increment,
decrement
}
}
}
是不是感觉清爽多了?代码量明显减少,而且逻辑更加清晰。
三、Pinia 相对于 Vuex 的优势:全方位碾压
为了让大家更直观地了解 Pinia 的优势,我做了个表格,对比一下 Pinia 和 Vuex:
特性 | Pinia | Vuex |
---|---|---|
API 设计 | 更加简洁、直观,贴近 Composition API | 较为繁琐,需要 Mutations |
TypeScript 支持 | 完美支持,类型推断强大 | 需要手动声明很多类型 |
Mutations | 不需要,直接用 Actions 修改 State | 必须使用 Mutations 修改 State |
Boilerplate 代码 | 更少,代码更简洁 | 更多,代码更冗余 |
模块化 | 更加灵活,可以创建多个 store | 需要使用 Modules,管理起来比较麻烦 |
Devtools 支持 | 更好,时间旅行调试更方便 | 也支持,但不如 Pinia 方便 |
Vue 3 支持 | 完美支持,与 Composition API 无缝集成 | 支持,但不如 Pinia 自然 |
官方地位 | 官方推荐的状态管理方案 | 之前是官方,现在 Pinia 逐渐取代 Vuex |
Size | 更小 | 稍大 |
从表格中可以看出,Pinia 在各个方面都优于 Vuex。
1. 更简洁的 API
Pinia 抛弃了 Mutations,直接在 Actions 中修改 State,简化了代码结构,提高了开发效率。
// Pinia
actions: {
increment() {
this.count++ // 直接修改 state
}
}
// Vuex
mutations: {
increment(state) {
state.count++
}
},
actions: {
increment({ commit }) {
commit('increment')
}
}
2. 更好的 TypeScript 支持
Pinia 对 TypeScript 的支持非常友好,类型推断非常强大,可以减少很多不必要的类型声明。
// Pinia
import { defineStore } from 'pinia'
interface State {
count: number
name: string
}
export const useCounterStore = defineStore('counter', {
state: (): State => ({
count: 0,
name: '老码农'
}),
getters: {
doubleCount: (state) => state.count * 2,
greeting: (state) => `Hello, ${state.name}!`
},
actions: {
increment() {
this.count++ // TypeScript 可以自动推断出 this.count 的类型
},
setName(name: string) {
this.name = name // TypeScript 可以自动检查参数类型
}
}
})
3. 更好的模块化
Pinia 的模块化更加灵活,可以创建多个 store,每个 store 都是一个独立的模块,易于维护和扩展。
// store/user.js
import { defineStore } from 'pinia'
export const useUserStore = defineStore('user', {
state: () => ({
name: '老码农',
age: 18
}),
actions: {
setName(name) {
this.name = name
}
}
})
// store/product.js
import { defineStore } from 'pinia'
export const useProductStore = defineStore('product', {
state: () => ({
products: []
}),
actions: {
fetchProducts() {
// ...
}
}
})
// 组件中使用
import { useUserStore } from '@/store/user'
import { useProductStore } from '@/store/product'
export default {
setup() {
const userStore = useUserStore()
const productStore = useProductStore()
return {
userStore,
productStore
}
}
}
4. 更好的 Devtools 支持
Pinia 的 Devtools 支持更加强大,可以方便地查看和修改 state,并且支持时间旅行调试,可以回溯到之前的状态。
5. 更小的 Size
Pinia 的体积比 Vuex 更小,可以减少应用的打包体积,提高加载速度。
四、Pinia 的设计哲学:大道至简
Pinia 的设计哲学可以用四个字概括:大道至简。
它追求的是简单、直观、易于理解和使用。
- 拥抱 Composition API: Pinia 与 Vue 3 的 Composition API 无缝集成,让状态管理更加自然。
- 抛弃 Mutations: Pinia 认为 Mutations 增加了代码的复杂性,没有必要,直接用 Actions 修改 State 更加简洁。
- 类型安全: Pinia 对 TypeScript 的支持非常重视,让开发者可以编写更加健壮的代码。
- 模块化: Pinia 的模块化设计更加灵活,可以创建多个 store,每个 store 都是一个独立的模块,易于维护和扩展。
Pinia 的目标是让状态管理变得更加简单、高效、可靠。
五、Pinia 的进阶使用:更上一层楼
除了基本的使用方法,Pinia 还有一些高级用法,可以帮助你更好地管理状态。
1. 使用 mapState
、mapGetters
、mapActions
类似于 Vuex,Pinia 也提供了 mapState
、mapGetters
、mapActions
等辅助函数,可以更方便地将 store 中的 state、getters、actions 映射到组件中。
// 组件中使用
import { mapState, mapGetters, mapActions } from 'pinia'
import { useCounterStore } from '@/store/counter'
export default {
computed: {
...mapState(useCounterStore, ['count']),
...mapGetters(useCounterStore, ['doubleCount'])
},
methods: {
...mapActions(useCounterStore, ['increment', 'decrement'])
},
setup() {
const counterStore = useCounterStore() // 仍然需要调用一次 store
return {
counterStore
}
}
}
2. 使用 $patch
方法
$patch
方法可以批量更新 state,提高性能。
// 修改单个属性
counterStore.$patch({ count: 10 })
// 使用函数修改多个属性
counterStore.$patch((state) => {
state.count++
state.name = '新名字'
})
3. 使用 $reset
方法
$reset
方法可以将 state 重置为初始值。
counterStore.$reset()
4. 订阅 State 的变化
可以使用 $subscribe
方法订阅 state 的变化,在 state 发生改变时执行回调函数。
counterStore.$subscribe((mutation, state) => {
// mutation:包含 state 变化的详细信息
// state:当前的 state
console.log('State changed:', mutation, state)
})
5. 在 Actions 中使用 await
Pinia 的 Actions 可以直接使用 await
,方便进行异步操作。
// store/user.js
import { defineStore } from 'pinia'
export const useUserStore = defineStore('user', {
state: () => ({
userInfo: null
}),
actions: {
async fetchUserInfo() {
const response = await fetch('/api/user')
this.userInfo = await response.json()
}
}
})
6. 使用插件
Pinia 支持插件,可以扩展 Pinia 的功能,例如持久化存储、日志记录等。
// main.js
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
import App from './App.vue'
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate) // 使用持久化插件
const app = createApp(App)
app.use(pinia)
app.mount('#app')
// store/counter.js
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0
}),
persist: true // 开启持久化
})
六、总结:拥抱 Pinia,迎接未来
总而言之,Pinia 是一个非常优秀的 Vue 3 状态管理库,它具有简单、直观、类型安全等优点,并且与 Vue 3 的 Composition API 无缝集成。
如果你正在使用 Vue 3,那么强烈建议你尝试一下 Pinia,相信你会爱上它的。
最后,我想说,技术是不断发展的,我们应该拥抱新技术,不断学习,才能保持竞争力。
今天的讲座就到这里,希望对大家有所帮助。 如果大家有问题,欢迎提问! 祝大家编码愉快,早日升职加薪!