本文章旨在快速帮助上手Pinia基础与核心部分,想要完整了解的请看官网或后续文章,Pinia 还可用于SSR。还有:这中文名可以叫菠萝,起源是西班牙语piña,中文译为菠萝。
Pinia概要:
Pinia一开始是为了探索Vuex的下一次迭代会是什么样子,它融合了Vuex 5核心团队讨论中的许多想法。
最终,我们意识到Pinia已经实现了Vuex 5中我们想要的大部分功能,并决定将 Pinia 作为新的推荐。
与 Vuex 相比,Pinia 提供了一个更简单的 API,没那么死板,提供了 Composition-API 风格的 API,
在与 TypeScript 一起使用时具有更好的类型推断支持。
什么是 Pinia Store ?
Store(比如Pinia)是一个独立存在的,它保存状态和业务逻辑,而不绑定到组件树。
换句话说,它是全局状态的主人。
它有点像一个组件,一直存在着,每个人都可以读写。
它包含三个概念,state、getters 和 actions,可以理解为这些概念相当于组件中的data、computed 和 methods。
什么时候应该使用 Store ?
存储应该包含可以在整个应用程序中访问的数据。
这包括在许多地方使用的数据,例如在导航栏中显示的用户信息,以及需要通过页面保存的数据,例如非常复杂的多步骤表单。
另一方面,您应该避免在 Store 中存储一个组件里的内部数据,例如组件模板内元素的可见性。
并非所有应用程序都需要访问全局状态,但如果您需要一个,Pinia会让你的开发更轻松。
Pinia 的主要 Api
* createPinia
- Plugins
* defineStore >> 声明一个Store
* storeToRefs >> 见`4.`使用案例
* mapState >> 在vue组件的options的computed中使用
* mapGetters >> mapState的别名,改用mapState
* mapActions >> 在vue组件的options的methods中使用
* getActivePinia >> 获取当前活动的 pinia 实例(如果有)。
- Store的核心配置:
+ State
+ Getters
+ Actions
vite + vue3 使用方式:
1. npm install pinia
(当前文章使用的最新版本:2.0.11,若pinia版本变动导致写法不一致,本文章会持续更新)
2. 创建一个 pinia(根存储)并将其传递给vue应用实例:
import { createApp,h } from 'vue'
import App from './App.vue'
import { createPinia } from 'pinia'
const app = createApp(App)
app.use(createPinia())
3. 定义 Store
在深入研究核心概念之前,我们需要知道 Pinia 是使用 defineStore() 定义的,并且它需要一个唯一的 name 作为第一个参数传递:
name(也称为id)是必需的,Pinia使用 name 参数将 Store 连接到 devtools。
将返回的函数命名为 `use…` 是一种跨组件的约定,使其用法规范化。
- defineStore(name,options)
- name: 必传,类型`string`
- options:{}
import { defineStore } from 'pinia'
// useStore可以是任何类似useUser、useCart的东西
// 第一个参数是应用程序中 Store 的唯一id
export const useStore = defineStore('main', {
// state: () => ({ count: 0 }),
state:()=>{
return {
// 所有这些属性都将自动推断其数据类型
items: [],
counter: 0,
name: 'Eduardo',
isAdmin: true,
}
},
getters: {
doubleCount: (state) => state.counter * 2,
//doubleCount(state){
// console.log(this,'想要在getter中获取该store的其他内容则不能用箭头函数,')
// return state.counter * 2
//},
},
actions: {
increment(num,test1,test2) {
console.log(num,test1,test2)
this.counter++
},
randomizeCounter() {
this.counter = Math.round(100 * Math.random())
},
}
})
4. 使用 Store
我们之所以声明store,是因为在`setup()`内部调用`useStore()`之前不会创建存储:
还有,不要看到不是<script setup>就吐槽这吐槽那,例子用什么都行,不要连抄都不会抄 [笑哭]
<template>
<p>Double count is {{ store.doubleCount }}</p>
</template>
<script>
import { mapState } from 'pinia'
import { useStore} from '@/stores/counter'
export default {
setup() {
const store = useStore()
store.counter++
store.$patch({ // 除了直接用store.counter++改变store之外,您还可以调用该$patch方法。state它允许您对部分对象同时应用多个更改:
counter: store.counter + 1,
name: 'Abalam',
})
store.$patch((state) => { // $patch 方法还接受一个函数,用于应对复杂数据的修改过程
state.items.push({ name: 'shoes', quantity: 1 })
state.isAdmin = false
})
store.increment(9,'测试多参数1','测试2') // 调用 actions 并传入参数
store.randomizeCounter() // 调用 actions
return {
// 您可以返回整个store实例以在模板中使用它
store,
}
},
computed: {
storeCounter() {
return this.store.counter * 3
},
// ...mapState(useCounterStore, ['counter']) // 没有 setup 的时候才使用这方式
},
}
</script>
定义多个 Store
这种基本完爆vuex的modules,任何不相关的模块直接单独定义一个新store。任意组件、任意store里想要使用任意一个或多个store都没问题。
您可以根据需要定义任意数量的Store,并且应该在不同的文件中定义每个Store以充分利用 pinia(例如自动允许您的包进行代码拆分和 TypeScript 推理)。
一旦 Store 被实例化,您就可以直接访问存储上的state、getter和actions中定义的任何属性。
// otherStore.ts
import { defineStore } from 'pinia'
export const useOtherStore = defineStore('otherStore', {
state: () => ({
count: 2
}),
getters: {
doubleCount: (state) => state.count * 2,
},
})
在其他store中使用otherStore ↓↓↓ >>> useOtherStore()
// index.ts
import { defineStore } from 'pinia'
import { useOtherStore } from './otherStore'
export const useStore = defineStore('main', {
state:()=>{
return {
// 所有这些属性都将自动推断其数据类型
items: [],
counter: 2,
name: 'Eduardo',
isAdmin: true,
}
},
getters: {
doubleCount(state) {
return this.counter * 2
},
},
actions: {
increment(num,test1,test2?:string) {
console.log(useOtherStore());
console.log(num,test1,test2)
this.counter++
},
randomizeCounter() {
this.counter = Math.round(100 * Math.random())
},
}
})
注意事项!
// 注意,store 是一个用 reactive 包装的对象,意味着不需要在 getters 后面写 .value ; // 所以就像 props 在 setup 中一样,不能对其进行 对象解构 操作! export default defineComponent({ setup(props) { const store = useStore() // ❌ 这不起作用,因为它会破坏响应式 >> This won't work because it breaks reactivity // 这跟从`props`解构的原理相同 >> it's the same as destructuring from `props` const { name, doubleCount } = store console.log(name) // "eduardo" console.log(doubleCount) // 2 return { // 永远都是"eduardo" >> will always be "eduardo" name, // will always be 2 doubleCount, // 这种就是正常的响应式数据 >> this one will be reactive doubleValue: computed(() => store.doubleCount), } }, })
为了从 Store 中提取属性同时保持其数据的响应性,您需要使用storeToRefs(). 它将为任何响应性属性创建 refs。
当您仅使用商店中的state但不调用任何action时,这很有用:
import { storeToRefs } from 'pinia'
export default defineComponent({
setup() {
const store = useStore()
// `name` 和 `doubleCount` 是响应式 refs 数据
// 这还将为插件添加的属性创建引用
// 但会跳过任何 action 和 不是reactive(非ref/reactive)的属性
const { name, doubleCount } = storeToRefs(store)
return {
name,
doubleCount
}
},
})
QQ交流群:522976012 ,欢迎来玩。
聚焦vue3,但不限于vue,任何前端问题,究其本质,值得讨论,研究与学习。