Pinia(皮尼亚)介绍
官方描述: The Vue Store that you will enjoy using
- Pinia 是一个轻量级的用于 Vue 的状态管理库,允许跨组件/页面共享状态。类似 Vuex, 是 Vue 的另一种状态管理工具
- Pinia 支持 Vue2 和 Vue3
Pinia文档有这么一段话
翻译过来:
Pinia 试图尽可能接近 Vuex 的理念。它旨在测试 Vuex 下一次迭代的提案,并且取得了成功,因为我们目前有一个针对 Vuex 5 的开放式 RFC,其 API 与[1]Pinia 使用的 API 非常相似[2]。请注意,Pinia 的作者 I (Eduardo) 是 Vue.js 核心团队的一员,并积极参与 Router 和 Vuex 等 API 的设计。我个人对这个项目的意图是重新设计使用全局 Store 的体验,同时保持 Vue 平易近人的哲学。我让 Pania 的 API 与 Vuex 一样接近,因为它不断向前发展,使人们可以轻松地迁移到 Vuex,甚至在未来融合这两个项目(在 Vuex 下)。
Vuex4一直被人诟病对于typescript的支持不良好,Vuex5也迟迟未来,所以有了Pinia的出现,所以现在学习Pinia,相当于提前学习Vuex5。还有什么理由不学呢?卷起来。
本文主要讲 Pinia 在 Vue3 中的使用, 在 Vue2 中使用略有差异,遇到的时候我也会同时列出来,更具体的使用,需要您查看Pinia官方文档
Pinia 优势
-
文档齐全,容易上手学习
-
极轻, 仅有 1 KB
-
模块化设计,便于拆分状态
-
支持多个Store
-
支持 Vue devtools、SSR 和 webpack 代码拆分
-
没有模块module的概念,不用面对一个store下嵌套着许多模块,使用单文件store(有点类似redux/toolkit的一个reducer),可以直接导入其他store使用
-
减去了mutations的概念,只保留state,getters和anctions三个概念,减少代码冗余
-
Pinia无需创建复杂的包装器来支持typescript,对于typescript类型判断是完全支持的,享受ide带来的自动补全,减少开发的心智负担,减少vue开发过程中的面向字符串编程
使用方法
1. 安装
yarn add pinia
# or with npm
npm install pinia
如果你的项目使用的是Vue2,需要另外安装composition api: @vue/composition-api。如果使用 了Nuxt,则应遵循这些说明。
如果你使用的是 Vue CLI,你可以试试这个非官方插件。
2.使用
Pinia是一个围绕Vue 3 Composition API的封装器。因此,你不必把它作为一个插件来初始化,除非你需要Vue devtools支持、SSR支持和webpack代码分割的情况:
创建一个 pinia(根存储)并将其传递给应用程序:
import { createPinia } from 'pinia'
app.use(createPinia())
使用上面的代码,将Pinia添加到Vue.js项目中,这样就可以在你的代码中使用全局的Pinia对象。
如果使用的是 Vue 2,还需要安装一个插件并将 created 注入pinia
到应用程序的根目录:
import { createPinia, PiniaVuePlugin } from 'pinia'
Vue.use(PiniaVuePlugin)
const pinia = createPinia()
new Vue({
el: '#app',
// other options...
// ...
// note the same `pinia` instance can be used across multiple Vue apps on
// the same page
pinia,
})
3.核心概念
创建store
// 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++
},
},
})
我们也可以使用函数(类似于组件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++
// with autocompletion ✨
counter.$patch({ count: counter.count + 1 })
// or using an action instead
counter.increment()
},
}
还可以以Composition Api形式用
// Composition Api
import { computed } from 'vue'
import { useCounterStore } from '../stores/counter'
export default {
name: 'HelloWorld',
setup() {
const counter = useCounterStore()
return {
// state 和 getter 都需要在使用 computed,这和 Options Api 一样
count: computed(() => counter.count),
increment: () => { counter.count++ }, // 可以直接修改 state 的值
increment: counter.increment, // 可以引用 store 中定义的 action
}
}
}
如果你不了解Composition API也没关系,Pinia可直接使用官方提供的 mapActions
和 mapState
方法,导出 store 中的 state、getter、action其用法与 Vuex 基本一致,很容易上手
//Options Api
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']),
},
}
Pinia的用法非常的多,我相信总有一种方式适合你。
以下是一种常见的todolist示例
import { defineStore } from 'pinia'
export const todos = defineStore('todos', {
state: () => ({
/** @type {{ text: string, id: number, isFinished: boolean }[]} */
todos: [],
/** @type {'all' | 'finished' | 'unfinished'} */
filter: 'all',
// type will be automatically inferred to number
nextId: 0,
}),
getters: {
finishedTodos(state) {
// autocompletion! ✨
return state.todos.filter((todo) => todo.isFinished)
},
unfinishedTodos(state) {
return state.todos.filter((todo) => !todo.isFinished)
},
/**
* @returns {{ text: string, id: number, isFinished: boolean }[]}
*/
filteredTodos(state) {
if (this.filter === 'finished') {
// call other getters with autocompletion ✨
return this.finishedTodos
} else if (this.filter === 'unfinished') {
return this.unfinishedTodos
}
return this.todos
},
},
actions: {
// any amount of arguments, return a promise or not
addTodo(text) {
// you can directly mutate the state
this.todos.push({ text, id: this.nextId++, isFinished: false })
},
},
})
4.性能
Pinia和Vuex都非常快,在某些情况下,使用Pinia的web应用程序会比使用Vuex更快。这种性能的提升可以归因于Pinia的极轻的重量,Pinia体积约1KB。
Pinia的学习和使用是相当友好的,看一遍官方文档就能上手,在上手过程中可以明显地感受到相对于vuex更加快捷,编码体验优秀。如果现在有新项目要使用 Vue3 进行开发,推荐无脑使用 Pinia。
详细使用请参考: 官方Demo